@burdenoff/microfe-billing 2026.915.3 → 2026.916.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -2,21 +2,21 @@ import { useBilling as e } from "../../../providers/BillingProvider.js";
|
|
|
2
2
|
import { AccessDenied as t } from "../../../shared/components/AccessDenied.js";
|
|
3
3
|
import { PageHeader as n } from "../../../shared/components/PageHeader.js";
|
|
4
4
|
import { StatCard as r } from "../../../shared/components/StatCard.js";
|
|
5
|
-
import { ServerError as
|
|
5
|
+
import { ServerError as ee } from "../../../shared/components/ServerError.js";
|
|
6
6
|
import "../../../shared/components/index.js";
|
|
7
|
-
import { formatCurrency as
|
|
8
|
-
import { isServerError as
|
|
7
|
+
import { formatCurrency as i, formatDateTime as a } from "../../../shared/utils/format.js";
|
|
8
|
+
import { isServerError as o } from "../../../shared/utils/error.js";
|
|
9
9
|
import "../../../shared/utils/index.js";
|
|
10
|
-
import { useBillingPermissions as
|
|
11
|
-
import { useGetDefaultBillingAccountSettingsQuery as
|
|
12
|
-
import { fetchDeveloperPortalRevenueDashboard as
|
|
13
|
-
import { useCallback as
|
|
14
|
-
import { Coins as
|
|
15
|
-
import { useSearchParams as
|
|
16
|
-
import { jsx as
|
|
17
|
-
import { EmphasisPanel as
|
|
10
|
+
import { useBillingPermissions as s } from "../../../hooks/useBillingPermissions.js";
|
|
11
|
+
import { useGetDefaultBillingAccountSettingsQuery as te } from "../../../../generated/global-operations.js";
|
|
12
|
+
import { fetchDeveloperPortalRevenueDashboard as c, fetchEarningsEntries as l, fetchEarningsSummary as u } from "../api.js";
|
|
13
|
+
import { useCallback as d, useEffect as f, useMemo as ne, useState as p } from "react";
|
|
14
|
+
import { ArrowLeft as m, Coins as h, HandCoins as g, RefreshCw as _, Wallet as v } from "lucide-react";
|
|
15
|
+
import { useSearchParams as re } from "react-router";
|
|
16
|
+
import { jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
17
|
+
import { EmphasisPanel as x, IllustratedEmptyState as S, PagePurpose as C, ResponsiveTable as w } from "@burdenoff/fe-libs/ui";
|
|
18
18
|
//#region src/billing/modules/earnings/pages/EarningsPage.tsx
|
|
19
|
-
var
|
|
19
|
+
var T = [
|
|
20
20
|
{
|
|
21
21
|
key: "application",
|
|
22
22
|
header: "Application",
|
|
@@ -31,17 +31,17 @@ var ee = [
|
|
|
31
31
|
{
|
|
32
32
|
key: "gross",
|
|
33
33
|
header: "Gross",
|
|
34
|
-
cell: (e) => /* @__PURE__ */
|
|
34
|
+
cell: (e) => /* @__PURE__ */ y("span", {
|
|
35
35
|
className: "tabular-nums",
|
|
36
|
-
children:
|
|
36
|
+
children: i(e.grossAmount, e.currency || "USD")
|
|
37
37
|
})
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
key: "net",
|
|
41
41
|
header: "Net",
|
|
42
|
-
cell: (e) => /* @__PURE__ */
|
|
42
|
+
cell: (e) => /* @__PURE__ */ y("span", {
|
|
43
43
|
className: "tabular-nums",
|
|
44
|
-
children:
|
|
44
|
+
children: i(e.netAmount, e.currency || "USD")
|
|
45
45
|
})
|
|
46
46
|
},
|
|
47
47
|
{
|
|
@@ -52,9 +52,9 @@ var ee = [
|
|
|
52
52
|
{
|
|
53
53
|
key: "purchased",
|
|
54
54
|
header: "Purchased",
|
|
55
|
-
cell: (e) =>
|
|
55
|
+
cell: (e) => a(e.purchasedAt)
|
|
56
56
|
}
|
|
57
|
-
],
|
|
57
|
+
], ie = [
|
|
58
58
|
{
|
|
59
59
|
key: "period",
|
|
60
60
|
header: "Period",
|
|
@@ -64,9 +64,9 @@ var ee = [
|
|
|
64
64
|
{
|
|
65
65
|
key: "amount",
|
|
66
66
|
header: "Amount",
|
|
67
|
-
cell: (e) => /* @__PURE__ */
|
|
67
|
+
cell: (e) => /* @__PURE__ */ y("span", {
|
|
68
68
|
className: "tabular-nums",
|
|
69
|
-
children:
|
|
69
|
+
children: i(e.netAmount, e.currency || "USD")
|
|
70
70
|
})
|
|
71
71
|
},
|
|
72
72
|
{
|
|
@@ -77,14 +77,14 @@ var ee = [
|
|
|
77
77
|
{
|
|
78
78
|
key: "scheduled",
|
|
79
79
|
header: "Scheduled",
|
|
80
|
-
cell: (e) => e.scheduledAt ?
|
|
80
|
+
cell: (e) => e.scheduledAt ? a(e.scheduledAt) : "-"
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
83
|
key: "completed",
|
|
84
84
|
header: "Completed",
|
|
85
|
-
cell: (e) => e.completedAt ?
|
|
85
|
+
cell: (e) => e.completedAt ? a(e.completedAt) : "-"
|
|
86
86
|
}
|
|
87
|
-
],
|
|
87
|
+
], ae = [
|
|
88
88
|
{
|
|
89
89
|
key: "category",
|
|
90
90
|
header: "Category",
|
|
@@ -104,9 +104,9 @@ var ee = [
|
|
|
104
104
|
{
|
|
105
105
|
key: "amount",
|
|
106
106
|
header: "Amount",
|
|
107
|
-
cell: (e) => /* @__PURE__ */
|
|
107
|
+
cell: (e) => /* @__PURE__ */ y("span", {
|
|
108
108
|
className: "tabular-nums",
|
|
109
|
-
children: e.unitType === "CREDITS" ? `${e.amount.toLocaleString()} credits` :
|
|
109
|
+
children: e.unitType === "CREDITS" ? `${e.amount.toLocaleString()} credits` : i(e.amount, e.currency || "USD")
|
|
110
110
|
})
|
|
111
111
|
},
|
|
112
112
|
{
|
|
@@ -117,29 +117,29 @@ var ee = [
|
|
|
117
117
|
{
|
|
118
118
|
key: "created",
|
|
119
119
|
header: "Created",
|
|
120
|
-
cell: (e) =>
|
|
120
|
+
cell: (e) => a(e.createdAt)
|
|
121
121
|
}
|
|
122
|
-
],
|
|
123
|
-
let [
|
|
124
|
-
apiGatewayUrl:
|
|
125
|
-
authToken:
|
|
126
|
-
orgId:
|
|
122
|
+
], E = () => {
|
|
123
|
+
let [a] = re(), E = s(), { apiGatewayUrl: D, authToken: O, orgId: k, navigate: A } = e(), j = a.get("developerOrgId"), M = a.get("developerOrgName"), N = a.get("returnTo"), { data: P, loading: F } = te({}), I = P?.getDefaultBillingAccount?.id, [L, R] = p(null), [z, B] = p([]), [V, H] = p(null), [U, W] = p("ALL"), [G, K] = p(!0), [q, J] = p(null), Y = ne(() => ({
|
|
124
|
+
apiGatewayUrl: D,
|
|
125
|
+
authToken: O,
|
|
126
|
+
orgId: k
|
|
127
127
|
}), [
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
]), X =
|
|
128
|
+
D,
|
|
129
|
+
O,
|
|
130
|
+
k
|
|
131
|
+
]), X = d(async () => {
|
|
132
132
|
K(!0), J(null);
|
|
133
133
|
try {
|
|
134
|
-
if (
|
|
135
|
-
H(await
|
|
134
|
+
if (j) {
|
|
135
|
+
H(await c(j, Y)), R(null), B([]);
|
|
136
136
|
return;
|
|
137
137
|
}
|
|
138
138
|
if (!I) {
|
|
139
139
|
R(null), B([]), H(null);
|
|
140
140
|
return;
|
|
141
141
|
}
|
|
142
|
-
let [e, t] = await Promise.all([
|
|
142
|
+
let [e, t] = await Promise.all([u(I, Y), l(I, Y)]);
|
|
143
143
|
R(e), B(t), H(null);
|
|
144
144
|
} catch (e) {
|
|
145
145
|
J(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to load earnings"));
|
|
@@ -148,166 +148,172 @@ var ee = [
|
|
|
148
148
|
}
|
|
149
149
|
}, [
|
|
150
150
|
I,
|
|
151
|
-
|
|
151
|
+
j,
|
|
152
152
|
Y
|
|
153
153
|
]);
|
|
154
|
-
if (
|
|
154
|
+
if (f(() => {
|
|
155
155
|
X();
|
|
156
|
-
}, [X]), !
|
|
157
|
-
if (!
|
|
156
|
+
}, [X]), !E.canViewBillingAccount) return /* @__PURE__ */ y(t, { message: "You don't have permission to view earnings." });
|
|
157
|
+
if (!j && !I && !F) return /* @__PURE__ */ y(S, {
|
|
158
158
|
illustration: "empty-generic",
|
|
159
159
|
title: "No billing account found",
|
|
160
160
|
description: "Create a billing account first to view earnings."
|
|
161
161
|
});
|
|
162
|
-
if (q &&
|
|
162
|
+
if (q && o(q)) return /* @__PURE__ */ y(ee, {
|
|
163
163
|
title: "Earnings unavailable",
|
|
164
164
|
message: q.message,
|
|
165
165
|
onRetry: () => void X(),
|
|
166
166
|
showRetry: !0
|
|
167
167
|
});
|
|
168
|
-
let
|
|
168
|
+
let oe = L ? [
|
|
169
169
|
{
|
|
170
170
|
title: "Available",
|
|
171
|
-
value:
|
|
172
|
-
icon: /* @__PURE__ */
|
|
171
|
+
value: i(L.availableAmount, "USD"),
|
|
172
|
+
icon: /* @__PURE__ */ y(g, { className: "size-5" })
|
|
173
173
|
},
|
|
174
174
|
{
|
|
175
175
|
title: "Reserved",
|
|
176
|
-
value:
|
|
177
|
-
icon: /* @__PURE__ */
|
|
176
|
+
value: i(L.reservedAmount, "USD"),
|
|
177
|
+
icon: /* @__PURE__ */ y(h, { className: "size-5" })
|
|
178
178
|
},
|
|
179
179
|
{
|
|
180
180
|
title: "Paid out",
|
|
181
|
-
value:
|
|
182
|
-
icon: /* @__PURE__ */
|
|
181
|
+
value: i(L.paidAmount, "USD"),
|
|
182
|
+
icon: /* @__PURE__ */ y(v, { className: "size-5" })
|
|
183
183
|
},
|
|
184
184
|
{
|
|
185
185
|
title: "Lifetime",
|
|
186
|
-
value:
|
|
187
|
-
icon: /* @__PURE__ */
|
|
186
|
+
value: i(L.totalLifetimeAmount, "USD"),
|
|
187
|
+
icon: /* @__PURE__ */ y(_, { className: "size-5" })
|
|
188
188
|
}
|
|
189
189
|
] : [], Z = U === "ALL" ? z : z.filter((e) => e.sourceCategory === U), Q = L?.categoryBreakdown?.map((e) => ({
|
|
190
190
|
title: `${e.category} earnings`,
|
|
191
|
-
value:
|
|
191
|
+
value: i(e.amount, "USD"),
|
|
192
192
|
helperText: `${e.count} entries`
|
|
193
|
-
})) ?? [],
|
|
193
|
+
})) ?? [], se = V?.summary ? [
|
|
194
194
|
{
|
|
195
195
|
title: "Gross revenue",
|
|
196
|
-
value:
|
|
197
|
-
icon: /* @__PURE__ */
|
|
196
|
+
value: i(V.summary.totalGrossRevenue, V.summary.currency),
|
|
197
|
+
icon: /* @__PURE__ */ y(g, { className: "size-5" })
|
|
198
198
|
},
|
|
199
199
|
{
|
|
200
200
|
title: "Net revenue",
|
|
201
|
-
value:
|
|
202
|
-
icon: /* @__PURE__ */
|
|
201
|
+
value: i(V.summary.totalNetRevenue, V.summary.currency),
|
|
202
|
+
icon: /* @__PURE__ */ y(v, { className: "size-5" })
|
|
203
203
|
},
|
|
204
204
|
{
|
|
205
205
|
title: "Pending balance",
|
|
206
|
-
value:
|
|
207
|
-
icon: /* @__PURE__ */
|
|
206
|
+
value: i(V.summary.pendingBalance, V.summary.currency),
|
|
207
|
+
icon: /* @__PURE__ */ y(h, { className: "size-5" })
|
|
208
208
|
},
|
|
209
209
|
{
|
|
210
210
|
title: "Platform fees",
|
|
211
|
-
value:
|
|
212
|
-
icon: /* @__PURE__ */
|
|
211
|
+
value: i(V.summary.totalPlatformFees, V.summary.currency),
|
|
212
|
+
icon: /* @__PURE__ */ y(_, { className: "size-5" })
|
|
213
213
|
}
|
|
214
|
-
] : [], $ = !!
|
|
215
|
-
return /* @__PURE__ */
|
|
214
|
+
] : [], $ = !!j;
|
|
215
|
+
return /* @__PURE__ */ b("div", {
|
|
216
216
|
className: "space-y-6 p-6",
|
|
217
217
|
children: [
|
|
218
|
-
/* @__PURE__ */
|
|
218
|
+
/* @__PURE__ */ y(n, {
|
|
219
219
|
title: $ ? "Marketplace earnings" : "Earnings",
|
|
220
|
-
description: $ ? `Developer portal revenue, sales, and payouts${
|
|
220
|
+
description: $ ? `Developer portal revenue, sales, and payouts${M ? ` for ${M}` : ""}.` : "Track affiliate, store, and other earning sources in one ledger.",
|
|
221
|
+
actions: N ? /* @__PURE__ */ b("button", {
|
|
222
|
+
type: "button",
|
|
223
|
+
onClick: () => A?.(N),
|
|
224
|
+
className: "inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary",
|
|
225
|
+
children: [/* @__PURE__ */ y(m, { className: "size-4" }), "Back"]
|
|
226
|
+
}) : void 0
|
|
221
227
|
}),
|
|
222
|
-
/* @__PURE__ */
|
|
223
|
-
G ? /* @__PURE__ */
|
|
228
|
+
/* @__PURE__ */ y(C, { 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." }),
|
|
229
|
+
G ? /* @__PURE__ */ y("div", {
|
|
224
230
|
className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4",
|
|
225
231
|
children: [
|
|
226
232
|
1,
|
|
227
233
|
2,
|
|
228
234
|
3,
|
|
229
235
|
4
|
|
230
|
-
].map((e) => /* @__PURE__ */
|
|
231
|
-
}) : $ ? V?.summary ? /* @__PURE__ */
|
|
236
|
+
].map((e) => /* @__PURE__ */ y("div", { className: "h-28 animate-pulse rounded-lg border border-border-subtle bg-bg-surface" }, e))
|
|
237
|
+
}) : $ ? V?.summary ? /* @__PURE__ */ b("div", {
|
|
232
238
|
className: "space-y-6",
|
|
233
239
|
children: [
|
|
234
|
-
/* @__PURE__ */
|
|
240
|
+
/* @__PURE__ */ y("div", {
|
|
235
241
|
className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4",
|
|
236
|
-
children:
|
|
242
|
+
children: se.map((e) => /* @__PURE__ */ y(r, {
|
|
237
243
|
label: e.title,
|
|
238
244
|
value: e.value,
|
|
239
245
|
icon: e.icon
|
|
240
246
|
}, e.title))
|
|
241
247
|
}),
|
|
242
|
-
/* @__PURE__ */
|
|
248
|
+
/* @__PURE__ */ b("section", {
|
|
243
249
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
244
|
-
children: [/* @__PURE__ */
|
|
250
|
+
children: [/* @__PURE__ */ b("div", {
|
|
245
251
|
className: "mb-4 flex items-center justify-between",
|
|
246
|
-
children: [/* @__PURE__ */
|
|
252
|
+
children: [/* @__PURE__ */ y("h2", {
|
|
247
253
|
className: "text-lg font-semibold text-text-primary",
|
|
248
254
|
children: "Recent sales"
|
|
249
|
-
}), /* @__PURE__ */
|
|
255
|
+
}), /* @__PURE__ */ b("button", {
|
|
250
256
|
type: "button",
|
|
251
257
|
onClick: () => void X(),
|
|
252
258
|
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__ */
|
|
259
|
+
children: [/* @__PURE__ */ y(_, { className: "size-4" }), "Refresh"]
|
|
254
260
|
})]
|
|
255
|
-
}), V.sales.length === 0 ? /* @__PURE__ */ S
|
|
261
|
+
}), V.sales.length === 0 ? /* @__PURE__ */ y(S, {
|
|
256
262
|
illustration: "empty-data",
|
|
257
263
|
title: "No sales yet",
|
|
258
264
|
description: "Sales will appear here when customers start purchasing your apps."
|
|
259
|
-
}) : /* @__PURE__ */
|
|
265
|
+
}) : /* @__PURE__ */ y(w, {
|
|
260
266
|
data: V.sales,
|
|
261
267
|
rowKey: (e) => e.id,
|
|
262
|
-
columns:
|
|
268
|
+
columns: T
|
|
263
269
|
})]
|
|
264
270
|
}),
|
|
265
|
-
/* @__PURE__ */
|
|
271
|
+
/* @__PURE__ */ b("section", {
|
|
266
272
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
267
|
-
children: [/* @__PURE__ */
|
|
273
|
+
children: [/* @__PURE__ */ y("h2", {
|
|
268
274
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
269
275
|
children: "Payouts"
|
|
270
|
-
}), V.payouts.length === 0 ? /* @__PURE__ */ S
|
|
276
|
+
}), V.payouts.length === 0 ? /* @__PURE__ */ y(S, {
|
|
271
277
|
illustration: "empty-data",
|
|
272
278
|
title: "No payouts yet",
|
|
273
279
|
description: "Scheduled and completed payouts will appear here."
|
|
274
|
-
}) : /* @__PURE__ */
|
|
280
|
+
}) : /* @__PURE__ */ y(w, {
|
|
275
281
|
data: V.payouts,
|
|
276
282
|
rowKey: (e) => e.id,
|
|
277
|
-
columns:
|
|
283
|
+
columns: ie
|
|
278
284
|
})]
|
|
279
285
|
})
|
|
280
286
|
]
|
|
281
|
-
}) : /* @__PURE__ */ S
|
|
287
|
+
}) : /* @__PURE__ */ y(S, {
|
|
282
288
|
illustration: "empty-data",
|
|
283
289
|
title: "No developer portal revenue yet",
|
|
284
290
|
description: "Marketplace revenue will appear here once your developer organization records sales."
|
|
285
|
-
}) : L ? /* @__PURE__ */
|
|
291
|
+
}) : L ? /* @__PURE__ */ b("div", {
|
|
286
292
|
className: "space-y-4",
|
|
287
|
-
children: [/* @__PURE__ */
|
|
293
|
+
children: [/* @__PURE__ */ y(x, {
|
|
288
294
|
className: "p-4 sm:p-5",
|
|
289
|
-
children: /* @__PURE__ */
|
|
295
|
+
children: /* @__PURE__ */ y("div", {
|
|
290
296
|
className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4",
|
|
291
|
-
children:
|
|
297
|
+
children: oe.map((e) => /* @__PURE__ */ y(r, {
|
|
292
298
|
label: e.title,
|
|
293
299
|
value: e.value,
|
|
294
300
|
icon: e.icon
|
|
295
301
|
}, e.title))
|
|
296
302
|
})
|
|
297
|
-
}), Q.length > 0 ? /* @__PURE__ */
|
|
303
|
+
}), Q.length > 0 ? /* @__PURE__ */ y("div", {
|
|
298
304
|
className: "grid gap-4 md:grid-cols-2 xl:grid-cols-3",
|
|
299
|
-
children: Q.map((e) => /* @__PURE__ */
|
|
305
|
+
children: Q.map((e) => /* @__PURE__ */ b("div", {
|
|
300
306
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
301
307
|
children: [
|
|
302
|
-
/* @__PURE__ */
|
|
308
|
+
/* @__PURE__ */ y("p", {
|
|
303
309
|
className: "text-sm text-text-secondary",
|
|
304
310
|
children: e.title
|
|
305
311
|
}),
|
|
306
|
-
/* @__PURE__ */
|
|
312
|
+
/* @__PURE__ */ y("p", {
|
|
307
313
|
className: "mt-2 text-xl font-semibold text-text-primary",
|
|
308
314
|
children: e.value
|
|
309
315
|
}),
|
|
310
|
-
/* @__PURE__ */
|
|
316
|
+
/* @__PURE__ */ y("p", {
|
|
311
317
|
className: "mt-1 text-xs text-text-secondary",
|
|
312
318
|
children: e.helperText
|
|
313
319
|
})
|
|
@@ -315,62 +321,62 @@ var ee = [
|
|
|
315
321
|
}, e.title))
|
|
316
322
|
}) : null]
|
|
317
323
|
}) : null,
|
|
318
|
-
q && !
|
|
324
|
+
q && !o(q) ? /* @__PURE__ */ y("div", {
|
|
319
325
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4 text-sm text-status-error-text",
|
|
320
326
|
children: q.message
|
|
321
327
|
}) : null,
|
|
322
|
-
/* @__PURE__ */
|
|
328
|
+
/* @__PURE__ */ b("section", {
|
|
323
329
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
324
|
-
children: [/* @__PURE__ */
|
|
330
|
+
children: [/* @__PURE__ */ b("div", {
|
|
325
331
|
className: "mb-4 flex items-center justify-between",
|
|
326
|
-
children: [/* @__PURE__ */
|
|
332
|
+
children: [/* @__PURE__ */ y("h2", {
|
|
327
333
|
className: "text-lg font-semibold text-text-primary",
|
|
328
334
|
children: "Ledger"
|
|
329
|
-
}), /* @__PURE__ */
|
|
335
|
+
}), /* @__PURE__ */ b("div", {
|
|
330
336
|
className: "flex items-center gap-3",
|
|
331
|
-
children: [/* @__PURE__ */
|
|
337
|
+
children: [/* @__PURE__ */ b("select", {
|
|
332
338
|
value: U,
|
|
333
339
|
onChange: (e) => W(e.target.value),
|
|
334
340
|
className: "rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary",
|
|
335
341
|
children: [
|
|
336
|
-
/* @__PURE__ */
|
|
342
|
+
/* @__PURE__ */ y("option", {
|
|
337
343
|
value: "ALL",
|
|
338
344
|
children: "All sources"
|
|
339
345
|
}),
|
|
340
|
-
/* @__PURE__ */
|
|
346
|
+
/* @__PURE__ */ y("option", {
|
|
341
347
|
value: "AFFILIATE",
|
|
342
348
|
children: "Affiliate"
|
|
343
349
|
}),
|
|
344
|
-
/* @__PURE__ */
|
|
350
|
+
/* @__PURE__ */ y("option", {
|
|
345
351
|
value: "STORE",
|
|
346
352
|
children: "Store"
|
|
347
353
|
}),
|
|
348
|
-
/* @__PURE__ */
|
|
354
|
+
/* @__PURE__ */ y("option", {
|
|
349
355
|
value: "OTHER",
|
|
350
356
|
children: "Other"
|
|
351
357
|
})
|
|
352
358
|
]
|
|
353
|
-
}), /* @__PURE__ */
|
|
359
|
+
}), /* @__PURE__ */ b("button", {
|
|
354
360
|
type: "button",
|
|
355
361
|
onClick: () => void X(),
|
|
356
362
|
className: "inline-flex items-center gap-2 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary",
|
|
357
|
-
children: [/* @__PURE__ */
|
|
363
|
+
children: [/* @__PURE__ */ y(_, { className: "size-4" }), "Refresh"]
|
|
358
364
|
})]
|
|
359
365
|
})]
|
|
360
|
-
}), Z.length === 0 && !G ? /* @__PURE__ */ S
|
|
366
|
+
}), Z.length === 0 && !G ? /* @__PURE__ */ y(S, {
|
|
361
367
|
illustration: "empty-data",
|
|
362
368
|
title: "No earnings yet",
|
|
363
369
|
description: "Eligible affiliate rewards and other earnings will appear here."
|
|
364
|
-
}) : /* @__PURE__ */
|
|
370
|
+
}) : /* @__PURE__ */ y(w, {
|
|
365
371
|
data: Z,
|
|
366
372
|
rowKey: (e) => e.id,
|
|
367
|
-
columns:
|
|
373
|
+
columns: ae
|
|
368
374
|
})]
|
|
369
375
|
})
|
|
370
376
|
]
|
|
371
377
|
});
|
|
372
378
|
};
|
|
373
379
|
//#endregion
|
|
374
|
-
export {
|
|
380
|
+
export { E as EarningsPage };
|
|
375
381
|
|
|
376
382
|
//# sourceMappingURL=EarningsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EarningsPage.js","names":[],"sources":["../../../../../src/billing/modules/earnings/pages/EarningsPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState, type FC } from 'react';\nimport { useSearchParams } from 'react-router';\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 = useCallback(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 }, [billingAccountId, developerOrgId, requestContext]);\n\n useEffect(() => {\n void loadData();\n }, [loadData]);\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(\n developerPortalRevenue.summary.totalGrossRevenue,\n developerPortalRevenue.summary.currency\n ),\n icon: <HandCoins className=\"size-5\" />,\n },\n {\n title: 'Net revenue',\n value: formatCurrency(\n developerPortalRevenue.summary.totalNetRevenue,\n developerPortalRevenue.summary.currency\n ),\n icon: <Wallet className=\"size-5\" />,\n },\n {\n title: 'Pending balance',\n value: formatCurrency(\n developerPortalRevenue.summary.pendingBalance,\n developerPortalRevenue.summary.currency\n ),\n icon: <Coins className=\"size-5\" />,\n },\n {\n title: 'Platform fees',\n value: formatCurrency(\n developerPortalRevenue.summary.totalPlatformFees,\n developerPortalRevenue.summary.currency\n ),\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":";;;;;;;;;;;;;;;;;;AA6BA,IAAM,KAAoE;CACxE;EACE,KAAK;EACL,QAAQ;EACR,UAAU;EACV,OAAO,MAAS,EAAK,aAAa,QAAQ,EAAK,aAAa,QAAQ;CACtE;CACA;EAAE,KAAK;EAAW,QAAQ;EAAW,OAAO,MAAS,EAAK;CAAa;CACvE;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MACL,kBAAC,QAAD;GAAM,WAAU;aACb,EAAe,EAAK,aAAa,EAAK,YAAY,KAAK;EACpD,CAAA;CAEV;CACA;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MACL,kBAAC,QAAD;GAAM,WAAU;aAAgB,EAAe,EAAK,WAAW,EAAK,YAAY,KAAK;EAAQ,CAAA;CAEjG;CACA;EAAE,KAAK;EAAU,QAAQ;EAAU,OAAO,MAAS,EAAK;CAAO;CAC/D;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MAAS,EAAe,EAAK,WAAW;CACjD;AACF,GAEM,IAAuE;CAC3E;EACE,KAAK;EACL,QAAQ;EACR,UAAU;EACV,OAAO,MAAW,EAAO,eAAe;CAC1C;CACA;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MACL,kBAAC,QAAD;GAAM,WAAU;aACb,EAAe,EAAO,WAAW,EAAO,YAAY,KAAK;EACtD,CAAA;CAEV;CACA;EAAE,KAAK;EAAU,QAAQ;EAAU,OAAO,MAAW,EAAO;CAAO;CACnE;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MAAY,EAAO,cAAc,EAAe,EAAO,WAAW,IAAI;CAC/E;CACA;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MAAY,EAAO,cAAc,EAAe,EAAO,WAAW,IAAI;CAC/E;AACF,GAEM,IAAwD;CAC5D;EACE,KAAK;EACL,QAAQ;EACR,UAAU;EACV,OAAO,MAAU,EAAM;CACzB;CACA;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MAAU,EAAM,eAAe,EAAM,qBAAqB;CACnE;CACA;EAAE,KAAK;EAAQ,QAAQ;EAAQ,OAAO,MAAU,EAAM;CAAS;CAC/D;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MACL,kBAAC,QAAD;GAAM,WAAU;aACb,EAAM,aAAa,YAChB,GAAG,EAAM,OAAO,eAAe,EAAE,YACjC,EAAe,EAAM,QAAQ,EAAM,YAAY,KAAK;EACpD,CAAA;CAEV;CACA;EAAE,KAAK;EAAU,QAAQ;EAAU,OAAO,MAAU,EAAM;CAAO;CACjE;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MAAU,EAAe,EAAM,SAAS;CACjD;AACF,GAEa,UAAyB;CACpC,IAAM,CAAC,KAAgB,EAAgB,GACjC,IAAc,EAAsB,GACpC,EAAE,kBAAe,cAAW,aAAU,EAAW,GACjD,IAAiB,EAAa,IAAI,gBAAgB,GAClD,IAAmB,EAAa,IAAI,kBAAkB,GACtD,EAAE,MAAM,IAAoB,SAAS,OACzC,EAAyC,CAAC,CAAC,GAEvC,IAAmB,IAAoB,0BAA0B,IACjE,CAAC,GAAS,KAAc,EAAiC,IAAI,GAC7D,CAAC,GAAS,KAAc,EAA0B,CAAC,CAAC,GACpD,CAAC,GAAwB,KAC7B,EAAiD,IAAI,GACjD,CAAC,GAAc,KAAmB,EAA+B,KAAK,GACtE,CAAC,GAAS,KAAc,EAAS,EAAI,GACrC,CAAC,GAAO,KAAY,EAAuB,IAAI,GAE/C,IAAiB,SACd;EACL;EACA;EACA;CACF,IACA;EAAC;EAAe;EAAW;CAAK,CAClC,GAEM,IAAW,EAAY,YAAY;EAEvC,AADA,EAAW,EAAI,GACf,EAAS,IAAI;EACb,IAAI;GACF,IAAI,GAAgB;IAOlB,AAFA,EAA0B,MAJE,EAC1B,GACA,CACF,CACuC,GACvC,EAAW,IAAI,GACf,EAAW,CAAC,CAAC;IACb;GACF;GAEA,IAAI,CAAC,GAAkB;IAGrB,AAFA,EAAW,IAAI,GACf,EAAW,CAAC,CAAC,GACb,EAA0B,IAAI;IAC9B;GACF;GAEA,IAAM,CAAC,GAAe,KAAiB,MAAM,QAAQ,IAAI,CACvD,EAAqB,GAAkB,CAAc,GACrD,EAAqB,GAAkB,CAAc,CACvD,CAAC;GAGD,AAFA,EAAW,CAAa,GACxB,EAAW,CAAa,GACxB,EAA0B,IAAI;EAChC,SAAS,GAAW;GAClB,EAAS,aAAqB,QAAQ,IAAY,gBAAI,MAAM,yBAAyB,CAAC;EACxF,UAAU;GACR,EAAW,EAAK;EAClB;CACF,GAAG;EAAC;EAAkB;EAAgB;CAAc,CAAC;CAMrD,IAJA,QAAgB;EACd,EAAc;CAChB,GAAG,CAAC,CAAQ,CAAC,GAET,CAAC,EAAY,uBACf,OAAO,kBAAC,GAAD,EAAc,SAAQ,8CAA+C,CAAA;CAG9E,IAAI,CAAC,KAAkB,CAAC,KAAoB,CAAC,IAC3C,OACE,kBAAC,GAAD;EACE,cAAa;EACb,OAAM;EACN,aAAY;CACb,CAAA;CAIL,IAAI,KAAS,EAAc,CAAK,GAC9B,OACE,kBAAC,GAAD;EACE,OAAM;EACN,SAAS,EAAM;EACf,eAAe,KAAK,EAAS;EAC7B,WAAA;CACD,CAAA;CAIL,IAAM,KAAQ,IACV;EACE;GACE,OAAO;GACP,OAAO,EAAe,EAAQ,iBAAiB,KAAK;GACpD,MAAM,kBAAC,GAAD,EAAW,WAAU,SAAU,CAAA;EACvC;EACA;GACE,OAAO;GACP,OAAO,EAAe,EAAQ,gBAAgB,KAAK;GACnD,MAAM,kBAAC,GAAD,EAAO,WAAU,SAAU,CAAA;EACnC;EACA;GACE,OAAO;GACP,OAAO,EAAe,EAAQ,YAAY,KAAK;GAC/C,MAAM,kBAAC,GAAD,EAAQ,WAAU,SAAU,CAAA;EACpC;EACA;GACE,OAAO;GACP,OAAO,EAAe,EAAQ,qBAAqB,KAAK;GACxD,MAAM,kBAAC,GAAD,EAAW,WAAU,SAAU,CAAA;EACvC;CACF,IACA,CAAC,GAEC,IACJ,MAAiB,QACb,IACA,EAAQ,QAAQ,MAAU,EAAM,mBAAmB,CAAY,GAE/D,IACJ,GAAS,mBAAmB,KAAK,OAAU;EACzC,OAAO,GAAG,EAAK,SAAS;EACxB,OAAO,EAAe,EAAK,QAAQ,KAAK;EACxC,YAAY,GAAG,EAAK,MAAM;CAC5B,EAAE,KAAK,CAAC,GAEJ,KAAuB,GAAwB,UACjD;EACE;GACE,OAAO;GACP,OAAO,EACL,EAAuB,QAAQ,mBAC/B,EAAuB,QAAQ,QACjC;GACA,MAAM,kBAAC,GAAD,EAAW,WAAU,SAAU,CAAA;EACvC;EACA;GACE,OAAO;GACP,OAAO,EACL,EAAuB,QAAQ,iBAC/B,EAAuB,QAAQ,QACjC;GACA,MAAM,kBAAC,GAAD,EAAQ,WAAU,SAAU,CAAA;EACpC;EACA;GACE,OAAO;GACP,OAAO,EACL,EAAuB,QAAQ,gBAC/B,EAAuB,QAAQ,QACjC;GACA,MAAM,kBAAC,GAAD,EAAO,WAAU,SAAU,CAAA;EACnC;EACA;GACE,OAAO;GACP,OAAO,EACL,EAAuB,QAAQ,mBAC/B,EAAuB,QAAQ,QACjC;GACA,MAAM,kBAAC,GAAD,EAAW,WAAU,SAAU,CAAA;EACvC;CACF,IACA,CAAC,GAEC,IAAwB,CAAC,CAAC;CAEhC,OACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD;IACE,OAAO,IAAwB,yBAAyB;IACxD,aACE,IACI,+CAA+C,IAAmB,QAAQ,MAAqB,GAAG,KAClG;GAEP,CAAA;GAED,kBAAC,GAAD,EAAA,UAAa,kOAIA,CAAA;GAEZ,IACC,kBAAC,OAAD;IAAK,WAAU;cACZ;KAAC;KAAG;KAAG;KAAG;IAAC,EAAE,KAAK,MACjB,kBAAC,OAAD,EAEE,WAAU,0EACX,GAFM,CAEN,CACF;GACE,CAAA,IACH,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;MAAO,GAApE,EAAK,KAA+D,CACpF;KACE,CAAA;KAEL,kBAAC,WAAD;MAAS,WAAU;gBAAnB,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBAA0C;OAAgB,CAAA,GACxE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,KAAK,EAAS;QAC7B,WAAU;kBAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,SAAU,CAAA,GAAC,SAE1B;SACL;UAEJ,EAAuB,MAAM,WAAW,IACvC,kBAAC,GAAD;OACE,cAAa;OACb,OAAM;OACN,aAAY;MACb,CAAA,IAED,kBAAC,GAAD;OACE,MAAM,EAAuB;OAC7B,SAAS,MAAS,EAAK;OACvB,SAAS;MACV,CAAA,CAEI;;KAET,kBAAC,WAAD;MAAS,WAAU;gBAAnB,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA+C;MAAW,CAAA,GAEvE,EAAuB,QAAQ,WAAW,IACzC,kBAAC,GAAD;OACE,cAAa;OACb,OAAM;OACN,aAAY;MACb,CAAA,IAED,kBAAC,GAAD;OACE,MAAM,EAAuB;OAC7B,SAAS,MAAW,EAAO;OAC3B,SAAS;MACV,CAAA,CAEI;;IACN;QAEL,kBAAC,GAAD;IACE,cAAa;IACb,OAAM;IACN,aAAY;GACb,CAAA,IAED,IACF,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAe,WAAU;eACvB,kBAAC,OAAD;MAAK,WAAU;gBACZ,GAAM,KAAK,MACV,kBAAC,GAAD;OAA2B,OAAO,EAAK;OAAO,OAAO,EAAK;OAAO,MAAM,EAAK;MAAO,GAApE,EAAK,KAA+D,CACpF;KACE,CAAA;IACQ,CAAA,GACd,EAAc,SAAS,IACtB,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAc,KAAK,MAClB,kBAAC,OAAD;MAEE,WAAU;gBAFZ;OAIE,kBAAC,KAAD;QAAG,WAAU;kBAA+B,EAAK;OAAS,CAAA;OAC1D,kBAAC,KAAD;QAAG,WAAU;kBAAgD,EAAK;OAAS,CAAA;OAC3E,kBAAC,KAAD;QAAG,WAAU;kBAAoC,EAAK;OAAc,CAAA;MACjE;QANE,EAAK,KAMP,CACN;IACE,CAAA,IACH,IACD;QACH;GAEH,KAAS,CAAC,EAAc,CAAK,IAC5B,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAM;GACJ,CAAA,IACH;GAEJ,kBAAC,WAAD;IAAS,WAAU;cAAnB,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA0C;KAAU,CAAA,GAClE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,OAAO;OACP,WAAW,MAAU,EAAgB,EAAM,OAAO,KAA6B;OAC/E,WAAU;iBAHZ;QAKE,kBAAC,UAAD;SAAQ,OAAM;mBAAM;QAAmB,CAAA;QACvC,kBAAC,UAAD;SAAQ,OAAM;mBAAY;QAAiB,CAAA;QAC3C,kBAAC,UAAD;SAAQ,OAAM;mBAAQ;QAAa,CAAA;QACnC,kBAAC,UAAD;SAAQ,OAAM;mBAAQ;QAAa,CAAA;OAC7B;UACR,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,KAAK,EAAS;OAC7B,WAAU;iBAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,SAAU,CAAA,GAAC,SAE1B;QACL;OACF;QAEJ,EAAgB,WAAW,KAAK,CAAC,IAChC,kBAAC,GAAD;KACE,cAAa;KACb,OAAM;KACN,aAAY;IACb,CAAA,IAED,kBAAC,GAAD;KACE,MAAM;KACN,SAAS,MAAU,EAAM;KACzB,SAAS;IACV,CAAA,CAEI;;EACN;;AAET"}
|
|
1
|
+
{"version":3,"file":"EarningsPage.js","names":[],"sources":["../../../../../src/billing/modules/earnings/pages/EarningsPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState, type FC } from 'react';\nimport { useSearchParams } from 'react-router';\nimport { ArrowLeft, 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, navigate } = useBilling();\n const developerOrgId = searchParams.get('developerOrgId');\n const developerOrgName = searchParams.get('developerOrgName');\n // Set by devportal's OrganizationDetailPage when it links here — lets this\n // page offer a way back without needing to know whether the org is\n // PHYSICAL or DIGITAL (that split isn't in scope on this side).\n const returnTo = searchParams.get('returnTo');\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 = useCallback(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 }, [billingAccountId, developerOrgId, requestContext]);\n\n useEffect(() => {\n void loadData();\n }, [loadData]);\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(\n developerPortalRevenue.summary.totalGrossRevenue,\n developerPortalRevenue.summary.currency\n ),\n icon: <HandCoins className=\"size-5\" />,\n },\n {\n title: 'Net revenue',\n value: formatCurrency(\n developerPortalRevenue.summary.totalNetRevenue,\n developerPortalRevenue.summary.currency\n ),\n icon: <Wallet className=\"size-5\" />,\n },\n {\n title: 'Pending balance',\n value: formatCurrency(\n developerPortalRevenue.summary.pendingBalance,\n developerPortalRevenue.summary.currency\n ),\n icon: <Coins className=\"size-5\" />,\n },\n {\n title: 'Platform fees',\n value: formatCurrency(\n developerPortalRevenue.summary.totalPlatformFees,\n developerPortalRevenue.summary.currency\n ),\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 actions={\n returnTo ? (\n <button\n type=\"button\"\n onClick={() => navigate?.(returnTo)}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n ) : undefined\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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"}
|