@burdenoff/microfe-billing 2026.625.4 → 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/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
|
@@ -2,21 +2,20 @@ 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 {
|
|
6
|
-
import { ServerError as a } from "../../../shared/components/ServerError.js";
|
|
5
|
+
import { ServerError as i } from "../../../shared/components/ServerError.js";
|
|
7
6
|
import "../../../shared/components/index.js";
|
|
8
|
-
import { useBillingEventEmitter as
|
|
9
|
-
import { formatCurrency as
|
|
10
|
-
import { isServerError as
|
|
7
|
+
import { useBillingEventEmitter as a } from "../../../hooks/useBillingEventEmitter.js";
|
|
8
|
+
import { formatCurrency as o, formatDateTime as s } from "../../../shared/utils/format.js";
|
|
9
|
+
import { isServerError as c } from "../../../shared/utils/error.js";
|
|
11
10
|
import "../../../shared/utils/index.js";
|
|
12
|
-
import { useBillingPermissions as
|
|
13
|
-
import { addAffiliateCodeNote as
|
|
14
|
-
import { useEffect as
|
|
15
|
-
import { Link2 as
|
|
16
|
-
import { jsx as
|
|
17
|
-
import { CTAOverflowMenu as A,
|
|
11
|
+
import { useBillingPermissions as l } from "../../../hooks/useBillingPermissions.js";
|
|
12
|
+
import { addAffiliateCodeNote as ee, createAffiliateCode as u, deactivateAffiliateCode as d, fetchMyAffiliateAnalytics as f, fetchMyAffiliateCodes as p, fetchMyAffiliateJourneys as m, updateAffiliateCode as h } from "../api.js";
|
|
13
|
+
import { useEffect as g, useMemo as _, useState as v } from "react";
|
|
14
|
+
import { Link2 as y, Plus as b, RefreshCw as x, StickyNote as S, Ticket as C, TrendingUp as w, Users as T } from "lucide-react";
|
|
15
|
+
import { jsx as E, jsxs as D } from "react/jsx-runtime";
|
|
16
|
+
import { CTAOverflowMenu as O, EmphasisPanel as k, IllustratedEmptyState as A, PagePurpose as j, ResponsiveTable as M } from "@burdenoff/fe-libs/ui";
|
|
18
17
|
//#region src/billing/modules/affiliate/pages/AffiliateDashboardPage.tsx
|
|
19
|
-
var
|
|
18
|
+
var N = [
|
|
20
19
|
{
|
|
21
20
|
key: "code",
|
|
22
21
|
header: "Code",
|
|
@@ -46,67 +45,67 @@ var M = [
|
|
|
46
45
|
{
|
|
47
46
|
key: "visited",
|
|
48
47
|
header: "Visited",
|
|
49
|
-
cell: (e) =>
|
|
48
|
+
cell: (e) => s(e.lastVisitedAt)
|
|
50
49
|
}
|
|
51
|
-
],
|
|
52
|
-
let
|
|
50
|
+
], P = () => {
|
|
51
|
+
let s = l(), { emit: P } = a(), { apiGatewayUrl: F, authToken: I, orgId: L } = e(), [R, z] = v([]), [B, V] = v([]), [H, te] = v(null), [U, W] = v(!0), [G, K] = v(null), [q, J] = v(null), [Y, X] = v({
|
|
53
52
|
label: "",
|
|
54
53
|
description: "",
|
|
55
54
|
notes: "",
|
|
56
55
|
productSlug: "",
|
|
57
56
|
destinationPath: "",
|
|
58
57
|
destinationUrl: ""
|
|
59
|
-
}), Z =
|
|
60
|
-
apiGatewayUrl:
|
|
61
|
-
authToken:
|
|
62
|
-
orgId:
|
|
58
|
+
}), Z = _(() => ({
|
|
59
|
+
apiGatewayUrl: F,
|
|
60
|
+
authToken: I,
|
|
61
|
+
orgId: L
|
|
63
62
|
}), [
|
|
64
|
-
P,
|
|
65
63
|
F,
|
|
66
|
-
I
|
|
64
|
+
I,
|
|
65
|
+
L
|
|
67
66
|
]), Q = async () => {
|
|
68
67
|
W(!0), K(null);
|
|
69
68
|
try {
|
|
70
69
|
let [e, t, n] = await Promise.all([
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
p(Z),
|
|
71
|
+
f(Z),
|
|
72
|
+
m(Z)
|
|
74
73
|
]);
|
|
75
|
-
|
|
74
|
+
z(e), te(t), V(n);
|
|
76
75
|
} catch (e) {
|
|
77
76
|
K(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to load affiliate dashboard"));
|
|
78
77
|
} finally {
|
|
79
78
|
W(!1);
|
|
80
79
|
}
|
|
81
80
|
};
|
|
82
|
-
if (
|
|
81
|
+
if (g(() => {
|
|
83
82
|
Q();
|
|
84
|
-
}, [Z]), !
|
|
85
|
-
if (G &&
|
|
83
|
+
}, [Z]), !s.canViewBillingAccount) return /* @__PURE__ */ E(t, { message: "You don't have permission to view affiliate analytics." });
|
|
84
|
+
if (G && c(G)) return /* @__PURE__ */ E(i, {
|
|
86
85
|
title: "Affiliate service unavailable",
|
|
87
86
|
message: G.message,
|
|
88
87
|
onRetry: () => void Q(),
|
|
89
88
|
showRetry: !0
|
|
90
89
|
});
|
|
91
|
-
let $ =
|
|
90
|
+
let $ = R.find((e) => e.id === q) ?? null, ne = async () => {
|
|
92
91
|
if (!Y.productSlug.trim()) {
|
|
93
92
|
K(/* @__PURE__ */ Error("Product slug is required"));
|
|
94
93
|
return;
|
|
95
94
|
}
|
|
96
95
|
try {
|
|
97
|
-
$ ? (await
|
|
96
|
+
$ ? (await h($.id, {
|
|
98
97
|
label: Y.label,
|
|
99
98
|
description: Y.description,
|
|
100
99
|
notes: Y.notes,
|
|
101
100
|
destinationPath: Y.destinationPath,
|
|
102
101
|
destinationUrl: Y.destinationUrl
|
|
103
|
-
}, Z),
|
|
102
|
+
}, Z), P("billing.affiliate_code.updated", {
|
|
104
103
|
route: "/billing/affiliate",
|
|
105
104
|
entityId: $.id,
|
|
106
105
|
source: "affiliate-dashboard"
|
|
107
|
-
})) :
|
|
106
|
+
})) : P("billing.affiliate_code.created", {
|
|
108
107
|
route: "/billing/affiliate",
|
|
109
|
-
entityId: (await
|
|
108
|
+
entityId: (await u({
|
|
110
109
|
label: Y.label,
|
|
111
110
|
description: Y.description,
|
|
112
111
|
notes: Y.notes,
|
|
@@ -126,7 +125,7 @@ var M = [
|
|
|
126
125
|
} catch (e) {
|
|
127
126
|
K(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to save affiliate code"));
|
|
128
127
|
}
|
|
129
|
-
},
|
|
128
|
+
}, re = (e) => {
|
|
130
129
|
J(e.id), X({
|
|
131
130
|
label: e.label ?? "",
|
|
132
131
|
description: e.description ?? "",
|
|
@@ -135,89 +134,93 @@ var M = [
|
|
|
135
134
|
destinationPath: e.destinationPath ?? "",
|
|
136
135
|
destinationUrl: e.destinationUrl ?? ""
|
|
137
136
|
});
|
|
138
|
-
},
|
|
139
|
-
await
|
|
137
|
+
}, ie = async (e) => {
|
|
138
|
+
await d(e, Z), P("billing.affiliate_code.deactivated", {
|
|
140
139
|
route: "/billing/affiliate",
|
|
141
140
|
entityId: e,
|
|
142
141
|
source: "affiliate-dashboard"
|
|
143
142
|
}), await Q();
|
|
144
|
-
},
|
|
143
|
+
}, ae = async (e) => {
|
|
145
144
|
let t = window.prompt("Add an internal note for this affiliate code");
|
|
146
|
-
t && (await
|
|
145
|
+
t && (await ee(e, t, Z), P("billing.affiliate_code.note_added", {
|
|
147
146
|
route: "/billing/affiliate",
|
|
148
147
|
entityId: e,
|
|
149
148
|
source: "affiliate-dashboard"
|
|
150
149
|
}), await Q());
|
|
151
|
-
},
|
|
150
|
+
}, oe = H ? [
|
|
152
151
|
{
|
|
153
152
|
title: "Codes",
|
|
154
|
-
value:
|
|
155
|
-
icon: /* @__PURE__ */
|
|
153
|
+
value: H.totalCodes,
|
|
154
|
+
icon: /* @__PURE__ */ E(C, { className: "size-5" })
|
|
156
155
|
},
|
|
157
156
|
{
|
|
158
157
|
title: "Visits",
|
|
159
|
-
value:
|
|
160
|
-
icon: /* @__PURE__ */
|
|
158
|
+
value: H.totalVisits,
|
|
159
|
+
icon: /* @__PURE__ */ E(T, { className: "size-5" })
|
|
161
160
|
},
|
|
162
161
|
{
|
|
163
162
|
title: "Journeys",
|
|
164
|
-
value:
|
|
165
|
-
icon: /* @__PURE__ */
|
|
163
|
+
value: H.totalJourneys,
|
|
164
|
+
icon: /* @__PURE__ */ E(y, { className: "size-5" })
|
|
166
165
|
},
|
|
167
166
|
{
|
|
168
167
|
title: "Revenue influenced",
|
|
169
|
-
value:
|
|
170
|
-
icon: /* @__PURE__ */
|
|
168
|
+
value: o(H.totalRevenueAmount, "USD"),
|
|
169
|
+
icon: /* @__PURE__ */ E(w, { className: "size-5" })
|
|
171
170
|
}
|
|
172
171
|
] : [];
|
|
173
|
-
return /* @__PURE__ */
|
|
172
|
+
return /* @__PURE__ */ D("div", {
|
|
174
173
|
className: "space-y-6 p-6",
|
|
175
174
|
children: [
|
|
176
|
-
/* @__PURE__ */
|
|
175
|
+
/* @__PURE__ */ E(n, {
|
|
177
176
|
title: "Affiliate codes",
|
|
178
177
|
description: "Create referral codes, add internal notes, and monitor attributed journeys.",
|
|
179
|
-
actions: /* @__PURE__ */ O
|
|
178
|
+
actions: /* @__PURE__ */ E(O, { actions: [{
|
|
180
179
|
label: "Refresh",
|
|
181
180
|
onSelect: () => void Q(),
|
|
182
|
-
icon: /* @__PURE__ */
|
|
181
|
+
icon: /* @__PURE__ */ E(x, { className: "size-4" })
|
|
183
182
|
}] })
|
|
184
183
|
}),
|
|
185
|
-
|
|
184
|
+
/* @__PURE__ */ E(j, { children: "Generate referral links for any product, share them, and see exactly what they drive — visits, signups, paid conversions and the rewards you have earned. Create and manage your codes on the left and watch attributed visitor journeys on the right." }),
|
|
185
|
+
U ? /* @__PURE__ */ E("div", {
|
|
186
186
|
className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4",
|
|
187
187
|
children: [
|
|
188
188
|
1,
|
|
189
189
|
2,
|
|
190
190
|
3,
|
|
191
191
|
4
|
|
192
|
-
].map((e) => /* @__PURE__ */
|
|
193
|
-
}) :
|
|
194
|
-
className: "
|
|
195
|
-
children:
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
192
|
+
].map((e) => /* @__PURE__ */ E("div", { className: "h-28 animate-pulse rounded-lg border border-border-subtle bg-bg-surface" }, e))
|
|
193
|
+
}) : H ? /* @__PURE__ */ E(k, {
|
|
194
|
+
className: "p-4 sm:p-5",
|
|
195
|
+
children: /* @__PURE__ */ E("div", {
|
|
196
|
+
className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4",
|
|
197
|
+
children: oe.map((e) => /* @__PURE__ */ E(r, {
|
|
198
|
+
label: e.title,
|
|
199
|
+
value: e.value,
|
|
200
|
+
icon: e.icon
|
|
201
|
+
}, e.title))
|
|
202
|
+
})
|
|
200
203
|
}) : null,
|
|
201
|
-
G && !
|
|
204
|
+
G && !c(G) ? /* @__PURE__ */ E("div", {
|
|
202
205
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4 text-sm text-status-error-text",
|
|
203
206
|
children: G.message
|
|
204
207
|
}) : null,
|
|
205
|
-
/* @__PURE__ */
|
|
208
|
+
/* @__PURE__ */ D("div", {
|
|
206
209
|
className: "grid gap-6 xl:grid-cols-[1.1fr,1.9fr]",
|
|
207
|
-
children: [/* @__PURE__ */
|
|
210
|
+
children: [/* @__PURE__ */ D("section", {
|
|
208
211
|
className: "rounded-lg border border-border-seam bg-bg-surface p-4 xl:sticky xl:top-6 xl:self-start",
|
|
209
212
|
children: [
|
|
210
|
-
/* @__PURE__ */
|
|
213
|
+
/* @__PURE__ */ D("div", {
|
|
211
214
|
className: "mb-4 flex items-center justify-between",
|
|
212
|
-
children: [/* @__PURE__ */
|
|
215
|
+
children: [/* @__PURE__ */ E("h2", {
|
|
213
216
|
className: "text-lg font-semibold text-text-primary",
|
|
214
217
|
children: $ ? "Update affiliate code" : "Create affiliate code"
|
|
215
|
-
}), $ ? null : /* @__PURE__ */
|
|
218
|
+
}), $ ? null : /* @__PURE__ */ E(b, { className: "size-4 text-text-secondary" })]
|
|
216
219
|
}),
|
|
217
|
-
/* @__PURE__ */
|
|
220
|
+
/* @__PURE__ */ D("div", {
|
|
218
221
|
className: "space-y-3",
|
|
219
222
|
children: [
|
|
220
|
-
/* @__PURE__ */
|
|
223
|
+
/* @__PURE__ */ E("input", {
|
|
221
224
|
value: Y.label,
|
|
222
225
|
onChange: (e) => X((t) => ({
|
|
223
226
|
...t,
|
|
@@ -226,7 +229,7 @@ var M = [
|
|
|
226
229
|
placeholder: "Label",
|
|
227
230
|
className: "w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary"
|
|
228
231
|
}),
|
|
229
|
-
/* @__PURE__ */
|
|
232
|
+
/* @__PURE__ */ E("input", {
|
|
230
233
|
value: Y.productSlug,
|
|
231
234
|
onChange: (e) => X((t) => ({
|
|
232
235
|
...t,
|
|
@@ -236,7 +239,7 @@ var M = [
|
|
|
236
239
|
className: "w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary",
|
|
237
240
|
disabled: !!$
|
|
238
241
|
}),
|
|
239
|
-
/* @__PURE__ */
|
|
242
|
+
/* @__PURE__ */ E("input", {
|
|
240
243
|
value: Y.destinationPath,
|
|
241
244
|
onChange: (e) => X((t) => ({
|
|
242
245
|
...t,
|
|
@@ -245,7 +248,7 @@ var M = [
|
|
|
245
248
|
placeholder: "Destination path (eg. /pricing)",
|
|
246
249
|
className: "w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary"
|
|
247
250
|
}),
|
|
248
|
-
/* @__PURE__ */
|
|
251
|
+
/* @__PURE__ */ E("input", {
|
|
249
252
|
value: Y.destinationUrl,
|
|
250
253
|
onChange: (e) => X((t) => ({
|
|
251
254
|
...t,
|
|
@@ -254,7 +257,7 @@ var M = [
|
|
|
254
257
|
placeholder: "Absolute destination URL (optional)",
|
|
255
258
|
className: "w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary"
|
|
256
259
|
}),
|
|
257
|
-
/* @__PURE__ */
|
|
260
|
+
/* @__PURE__ */ E("textarea", {
|
|
258
261
|
value: Y.description,
|
|
259
262
|
onChange: (e) => X((t) => ({
|
|
260
263
|
...t,
|
|
@@ -263,7 +266,7 @@ var M = [
|
|
|
263
266
|
placeholder: "Description",
|
|
264
267
|
className: "min-h-24 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary"
|
|
265
268
|
}),
|
|
266
|
-
/* @__PURE__ */
|
|
269
|
+
/* @__PURE__ */ E("textarea", {
|
|
267
270
|
value: Y.notes,
|
|
268
271
|
onChange: (e) => X((t) => ({
|
|
269
272
|
...t,
|
|
@@ -274,14 +277,14 @@ var M = [
|
|
|
274
277
|
})
|
|
275
278
|
]
|
|
276
279
|
}),
|
|
277
|
-
/* @__PURE__ */
|
|
280
|
+
/* @__PURE__ */ D("div", {
|
|
278
281
|
className: "mt-4 flex flex-wrap gap-3",
|
|
279
|
-
children: [/* @__PURE__ */
|
|
282
|
+
children: [/* @__PURE__ */ E("button", {
|
|
280
283
|
type: "button",
|
|
281
|
-
onClick: () => void
|
|
284
|
+
onClick: () => void ne(),
|
|
282
285
|
className: "rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text",
|
|
283
286
|
children: $ ? "Save changes" : "Create code"
|
|
284
|
-
}), $ ? /* @__PURE__ */
|
|
287
|
+
}), $ ? /* @__PURE__ */ E("button", {
|
|
285
288
|
type: "button",
|
|
286
289
|
onClick: () => {
|
|
287
290
|
J(null), X({
|
|
@@ -298,32 +301,32 @@ var M = [
|
|
|
298
301
|
}) : null]
|
|
299
302
|
})
|
|
300
303
|
]
|
|
301
|
-
}), /* @__PURE__ */
|
|
304
|
+
}), /* @__PURE__ */ D("section", {
|
|
302
305
|
className: "space-y-4",
|
|
303
|
-
children: [/* @__PURE__ */
|
|
306
|
+
children: [/* @__PURE__ */ D("div", {
|
|
304
307
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
305
|
-
children: [/* @__PURE__ */
|
|
308
|
+
children: [/* @__PURE__ */ E("h2", {
|
|
306
309
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
307
310
|
children: "My codes"
|
|
308
|
-
}),
|
|
309
|
-
|
|
311
|
+
}), R.length === 0 && !U ? /* @__PURE__ */ E(A, {
|
|
312
|
+
illustration: "empty-data",
|
|
310
313
|
title: "No affiliate codes yet",
|
|
311
314
|
description: "Create your first code to start tracking attributable journeys."
|
|
312
|
-
}) : /* @__PURE__ */
|
|
315
|
+
}) : /* @__PURE__ */ E("div", {
|
|
313
316
|
className: "space-y-4",
|
|
314
|
-
children:
|
|
317
|
+
children: R.map((e) => {
|
|
315
318
|
let t = e.destinationUrl || `https://${e.productSlug}.com${e.destinationPath || ""}?ref=${e.code}`;
|
|
316
|
-
return /* @__PURE__ */
|
|
319
|
+
return /* @__PURE__ */ D("div", {
|
|
317
320
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
318
321
|
children: [
|
|
319
|
-
/* @__PURE__ */
|
|
322
|
+
/* @__PURE__ */ D("div", {
|
|
320
323
|
className: "flex flex-wrap items-start justify-between gap-4",
|
|
321
|
-
children: [/* @__PURE__ */
|
|
322
|
-
/* @__PURE__ */
|
|
324
|
+
children: [/* @__PURE__ */ D("div", { children: [
|
|
325
|
+
/* @__PURE__ */ E("p", {
|
|
323
326
|
className: "text-sm font-semibold text-text-primary",
|
|
324
327
|
children: e.label || e.code
|
|
325
328
|
}),
|
|
326
|
-
/* @__PURE__ */
|
|
329
|
+
/* @__PURE__ */ D("p", {
|
|
327
330
|
className: "text-xs text-text-secondary",
|
|
328
331
|
children: [
|
|
329
332
|
e.code,
|
|
@@ -331,80 +334,80 @@ var M = [
|
|
|
331
334
|
e.productSlug
|
|
332
335
|
]
|
|
333
336
|
}),
|
|
334
|
-
/* @__PURE__ */
|
|
337
|
+
/* @__PURE__ */ E("p", {
|
|
335
338
|
className: "mt-1 break-all text-xs text-text-secondary",
|
|
336
339
|
children: t
|
|
337
340
|
})
|
|
338
|
-
] }), /* @__PURE__ */
|
|
341
|
+
] }), /* @__PURE__ */ D("div", {
|
|
339
342
|
className: "flex flex-wrap gap-2",
|
|
340
343
|
children: [
|
|
341
|
-
/* @__PURE__ */
|
|
344
|
+
/* @__PURE__ */ E("button", {
|
|
342
345
|
type: "button",
|
|
343
|
-
onClick: () =>
|
|
346
|
+
onClick: () => re(e),
|
|
344
347
|
className: "rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary",
|
|
345
348
|
children: "Edit"
|
|
346
349
|
}),
|
|
347
|
-
/* @__PURE__ */
|
|
350
|
+
/* @__PURE__ */ D("button", {
|
|
348
351
|
type: "button",
|
|
349
|
-
onClick: () => void
|
|
352
|
+
onClick: () => void ae(e.id),
|
|
350
353
|
className: "inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary",
|
|
351
|
-
children: [/* @__PURE__ */
|
|
354
|
+
children: [/* @__PURE__ */ E(S, { className: "size-3.5" }), "Note"]
|
|
352
355
|
}),
|
|
353
|
-
e.status === "ACTIVE" ? /* @__PURE__ */
|
|
356
|
+
e.status === "ACTIVE" ? /* @__PURE__ */ E("button", {
|
|
354
357
|
type: "button",
|
|
355
|
-
onClick: () => void
|
|
358
|
+
onClick: () => void ie(e.id),
|
|
356
359
|
className: "rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-error-text",
|
|
357
360
|
children: "Deactivate"
|
|
358
361
|
}) : null
|
|
359
362
|
]
|
|
360
363
|
})]
|
|
361
364
|
}),
|
|
362
|
-
/* @__PURE__ */
|
|
365
|
+
/* @__PURE__ */ D("div", {
|
|
363
366
|
className: "mt-4 grid gap-3 sm:grid-cols-2 xl:grid-cols-4",
|
|
364
367
|
children: [
|
|
365
|
-
/* @__PURE__ */
|
|
368
|
+
/* @__PURE__ */ D("div", {
|
|
366
369
|
className: "rounded-md border border-border-subtle bg-bg-surface p-3",
|
|
367
|
-
children: [/* @__PURE__ */
|
|
370
|
+
children: [/* @__PURE__ */ E("p", {
|
|
368
371
|
className: "text-xs text-text-secondary",
|
|
369
372
|
children: "Visits"
|
|
370
|
-
}), /* @__PURE__ */
|
|
373
|
+
}), /* @__PURE__ */ E("p", {
|
|
371
374
|
className: "text-lg font-semibold text-text-primary",
|
|
372
375
|
children: e.totalVisits
|
|
373
376
|
})]
|
|
374
377
|
}),
|
|
375
|
-
/* @__PURE__ */
|
|
378
|
+
/* @__PURE__ */ D("div", {
|
|
376
379
|
className: "rounded-md border border-border-subtle bg-bg-surface p-3",
|
|
377
|
-
children: [/* @__PURE__ */
|
|
380
|
+
children: [/* @__PURE__ */ E("p", {
|
|
378
381
|
className: "text-xs text-text-secondary",
|
|
379
382
|
children: "Signups"
|
|
380
|
-
}), /* @__PURE__ */
|
|
383
|
+
}), /* @__PURE__ */ E("p", {
|
|
381
384
|
className: "text-lg font-semibold text-text-primary",
|
|
382
385
|
children: e.totalSignups
|
|
383
386
|
})]
|
|
384
387
|
}),
|
|
385
|
-
/* @__PURE__ */
|
|
388
|
+
/* @__PURE__ */ D("div", {
|
|
386
389
|
className: "rounded-md border border-border-subtle bg-bg-surface p-3",
|
|
387
|
-
children: [/* @__PURE__ */
|
|
390
|
+
children: [/* @__PURE__ */ E("p", {
|
|
388
391
|
className: "text-xs text-text-secondary",
|
|
389
392
|
children: "Paid conversions"
|
|
390
|
-
}), /* @__PURE__ */
|
|
393
|
+
}), /* @__PURE__ */ E("p", {
|
|
391
394
|
className: "text-lg font-semibold text-text-primary",
|
|
392
395
|
children: e.totalPaidConversions + e.totalStorePurchases
|
|
393
396
|
})]
|
|
394
397
|
}),
|
|
395
|
-
/* @__PURE__ */
|
|
398
|
+
/* @__PURE__ */ D("div", {
|
|
396
399
|
className: "rounded-md border border-border-subtle bg-bg-surface p-3",
|
|
397
|
-
children: [/* @__PURE__ */
|
|
400
|
+
children: [/* @__PURE__ */ E("p", {
|
|
398
401
|
className: "text-xs text-text-secondary",
|
|
399
402
|
children: "Rewards earned"
|
|
400
|
-
}), /* @__PURE__ */
|
|
403
|
+
}), /* @__PURE__ */ E("p", {
|
|
401
404
|
className: "text-lg font-semibold text-text-primary",
|
|
402
|
-
children:
|
|
405
|
+
children: o(e.totalRewardAmount, "USD")
|
|
403
406
|
})]
|
|
404
407
|
})
|
|
405
408
|
]
|
|
406
409
|
}),
|
|
407
|
-
e.notes ? /* @__PURE__ */
|
|
410
|
+
e.notes ? /* @__PURE__ */ E("p", {
|
|
408
411
|
className: "mt-3 text-sm text-text-secondary",
|
|
409
412
|
children: e.notes
|
|
410
413
|
}) : null
|
|
@@ -412,19 +415,19 @@ var M = [
|
|
|
412
415
|
}, e.id);
|
|
413
416
|
})
|
|
414
417
|
})]
|
|
415
|
-
}), /* @__PURE__ */
|
|
418
|
+
}), /* @__PURE__ */ D("div", {
|
|
416
419
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
417
|
-
children: [/* @__PURE__ */
|
|
420
|
+
children: [/* @__PURE__ */ E("h2", {
|
|
418
421
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
419
422
|
children: "Recent attributed journeys"
|
|
420
|
-
}),
|
|
421
|
-
|
|
423
|
+
}), B.length === 0 ? /* @__PURE__ */ E(A, {
|
|
424
|
+
illustration: "empty-people",
|
|
422
425
|
title: "No journeys yet",
|
|
423
426
|
description: "Journeys will appear as visitors land on your affiliate links."
|
|
424
|
-
}) : /* @__PURE__ */
|
|
425
|
-
data:
|
|
427
|
+
}) : /* @__PURE__ */ E(M, {
|
|
428
|
+
data: B,
|
|
426
429
|
rowKey: (e) => e.id,
|
|
427
|
-
columns:
|
|
430
|
+
columns: N
|
|
428
431
|
})]
|
|
429
432
|
})]
|
|
430
433
|
})]
|
|
@@ -433,6 +436,6 @@ var M = [
|
|
|
433
436
|
});
|
|
434
437
|
};
|
|
435
438
|
//#endregion
|
|
436
|
-
export {
|
|
439
|
+
export { P as AffiliateDashboardPage };
|
|
437
440
|
|
|
438
441
|
//# sourceMappingURL=AffiliateDashboardPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AffiliateDashboardPage.js","names":[],"sources":["../../../../../src/billing/modules/affiliate/pages/AffiliateDashboardPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState, type FC } from 'react';\nimport { Link2, Plus, RefreshCw, StickyNote, Ticket, TrendingUp, Users } from 'lucide-react';\nimport { ResponsiveTable, type ResponsiveTableColumn } from '@burdenoff/fe-libs/ui';\n\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport {\n AccessDenied,\n EmptyState,\n PageHeader,\n ServerError,\n StatCard,\n} from '../../../shared/components';\nimport { CTAOverflowMenu } from '@burdenoff/fe-libs/ui';\nimport { formatCurrency, formatDateTime, isServerError } from '../../../shared/utils';\n\nconst journeyColumns: ResponsiveTableColumn<AffiliateJourney>[] = [\n { key: 'code', header: 'Code', priority: 'primary', cell: (journey) => journey.codeValue },\n { key: 'product', header: 'Product', cell: (journey) => journey.productSlug },\n {\n key: 'path',\n header: 'Path',\n cell: (journey) => journey.currentPath || journey.landingPath || '-',\n },\n { key: 'country', header: 'Country', cell: (journey) => journey.geographyCountry || '-' },\n { key: 'status', header: 'Status', cell: (journey) => journey.status },\n {\n key: 'visited',\n header: 'Visited',\n cell: (journey) => formatDateTime(journey.lastVisitedAt),\n },\n];\nimport {\n addAffiliateCodeNote,\n createAffiliateCode,\n deactivateAffiliateCode,\n fetchMyAffiliateAnalytics,\n fetchMyAffiliateCodes,\n fetchMyAffiliateJourneys,\n updateAffiliateCode,\n type AffiliateCode,\n type AffiliateJourney,\n type AffiliateAnalyticsSummary,\n} from '../api';\n\nexport const AffiliateDashboardPage: FC = () => {\n const permissions = useBillingPermissions();\n const { emit } = useBillingEventEmitter();\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n\n const [codes, setCodes] = useState<AffiliateCode[]>([]);\n const [journeys, setJourneys] = useState<AffiliateJourney[]>([]);\n const [analytics, setAnalytics] = useState<AffiliateAnalyticsSummary | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const [selectedCodeId, setSelectedCodeId] = useState<string | null>(null);\n const [form, setForm] = useState({\n label: '',\n description: '',\n notes: '',\n productSlug: '',\n destinationPath: '',\n destinationUrl: '',\n });\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 const [loadedCodes, loadedAnalytics, loadedJourneys] = await Promise.all([\n fetchMyAffiliateCodes(requestContext),\n fetchMyAffiliateAnalytics(requestContext),\n fetchMyAffiliateJourneys(requestContext),\n ]);\n setCodes(loadedCodes);\n setAnalytics(loadedAnalytics);\n setJourneys(loadedJourneys);\n } catch (loadError) {\n setError(\n loadError instanceof Error ? loadError : new Error('Failed to load affiliate dashboard')\n );\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void loadData();\n }, [requestContext]);\n\n if (!permissions.canViewBillingAccount) {\n return <AccessDenied message=\"You don't have permission to view affiliate analytics.\" />;\n }\n\n if (error && isServerError(error)) {\n return (\n <ServerError\n title=\"Affiliate service unavailable\"\n message={error.message}\n onRetry={() => void loadData()}\n showRetry\n />\n );\n }\n\n const selectedCode = codes.find((item) => item.id === selectedCodeId) ?? null;\n\n const handleSubmit = async () => {\n if (!form.productSlug.trim()) {\n setError(new Error('Product slug is required'));\n return;\n }\n\n try {\n if (selectedCode) {\n await updateAffiliateCode(\n selectedCode.id,\n {\n label: form.label,\n description: form.description,\n notes: form.notes,\n destinationPath: form.destinationPath,\n destinationUrl: form.destinationUrl,\n },\n requestContext\n );\n emit('billing.affiliate_code.updated', {\n route: '/billing/affiliate',\n entityId: selectedCode.id,\n source: 'affiliate-dashboard',\n });\n } else {\n const createdCode = await createAffiliateCode(\n {\n label: form.label,\n description: form.description,\n notes: form.notes,\n productSlug: form.productSlug,\n destinationPath: form.destinationPath,\n destinationUrl: form.destinationUrl,\n },\n requestContext\n );\n emit('billing.affiliate_code.created', {\n route: '/billing/affiliate',\n entityId: createdCode.id,\n source: 'affiliate-dashboard',\n });\n }\n\n setSelectedCodeId(null);\n setForm({\n label: '',\n description: '',\n notes: '',\n productSlug: '',\n destinationPath: '',\n destinationUrl: '',\n });\n await loadData();\n } catch (submitError) {\n setError(\n submitError instanceof Error ? submitError : new Error('Failed to save affiliate code')\n );\n }\n };\n\n const handleEdit = (code: AffiliateCode) => {\n setSelectedCodeId(code.id);\n setForm({\n label: code.label ?? '',\n description: code.description ?? '',\n notes: code.notes ?? '',\n productSlug: code.productSlug,\n destinationPath: code.destinationPath ?? '',\n destinationUrl: code.destinationUrl ?? '',\n });\n };\n\n const handleDeactivate = async (codeId: string) => {\n await deactivateAffiliateCode(codeId, requestContext);\n emit('billing.affiliate_code.deactivated', {\n route: '/billing/affiliate',\n entityId: codeId,\n source: 'affiliate-dashboard',\n });\n await loadData();\n };\n\n const handleQuickNote = async (codeId: string) => {\n const note = window.prompt('Add an internal note for this affiliate code');\n if (!note) return;\n await addAffiliateCodeNote(codeId, note, requestContext);\n emit('billing.affiliate_code.note_added', {\n route: '/billing/affiliate',\n entityId: codeId,\n source: 'affiliate-dashboard',\n });\n await loadData();\n };\n\n const metricCards = analytics\n ? [\n { title: 'Codes', value: analytics.totalCodes, icon: <Ticket className=\"size-5\" /> },\n { title: 'Visits', value: analytics.totalVisits, icon: <Users className=\"size-5\" /> },\n { title: 'Journeys', value: analytics.totalJourneys, icon: <Link2 className=\"size-5\" /> },\n {\n title: 'Revenue influenced',\n value: formatCurrency(analytics.totalRevenueAmount, 'USD'),\n icon: <TrendingUp className=\"size-5\" />,\n },\n ]\n : [];\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title=\"Affiliate codes\"\n description=\"Create referral codes, add internal notes, and monitor attributed journeys.\"\n actions={\n <CTAOverflowMenu\n actions={[\n {\n label: 'Refresh',\n onSelect: () => void loadData(),\n icon: <RefreshCw className=\"size-4\" />,\n },\n ]}\n />\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 ) : analytics ? (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n {metricCards.map((card) => (\n <StatCard key={card.title} label={card.title} value={card.value} icon={card.icon} />\n ))}\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 <div className=\"grid gap-6 xl:grid-cols-[1.1fr,1.9fr]\">\n <section className=\"rounded-lg border border-border-seam bg-bg-surface p-4 xl:sticky xl:top-6 xl:self-start\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {selectedCode ? 'Update affiliate code' : 'Create affiliate code'}\n </h2>\n {!selectedCode ? <Plus className=\"size-4 text-text-secondary\" /> : null}\n </div>\n\n <div className=\"space-y-3\">\n <input\n value={form.label}\n onChange={(event) =>\n setForm((current) => ({ ...current, label: event.target.value }))\n }\n placeholder=\"Label\"\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n <input\n value={form.productSlug}\n onChange={(event) =>\n setForm((current) => ({ ...current, productSlug: event.target.value }))\n }\n placeholder=\"Product slug (eg. bigconsole)\"\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n disabled={Boolean(selectedCode)}\n />\n <input\n value={form.destinationPath}\n onChange={(event) =>\n setForm((current) => ({ ...current, destinationPath: event.target.value }))\n }\n placeholder=\"Destination path (eg. /pricing)\"\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n <input\n value={form.destinationUrl}\n onChange={(event) =>\n setForm((current) => ({ ...current, destinationUrl: event.target.value }))\n }\n placeholder=\"Absolute destination URL (optional)\"\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n <textarea\n value={form.description}\n onChange={(event) =>\n setForm((current) => ({ ...current, description: event.target.value }))\n }\n placeholder=\"Description\"\n className=\"min-h-24 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n <textarea\n value={form.notes}\n onChange={(event) =>\n setForm((current) => ({ ...current, notes: event.target.value }))\n }\n placeholder=\"Internal notes\"\n className=\"min-h-24 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </div>\n\n <div className=\"mt-4 flex flex-wrap gap-3\">\n <button\n type=\"button\"\n onClick={() => void handleSubmit()}\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text\"\n >\n {selectedCode ? 'Save changes' : 'Create code'}\n </button>\n {selectedCode ? (\n <button\n type=\"button\"\n onClick={() => {\n setSelectedCodeId(null);\n setForm({\n label: '',\n description: '',\n notes: '',\n productSlug: '',\n destinationPath: '',\n destinationUrl: '',\n });\n }}\n className=\"rounded-md border border-border-subtle bg-bg-surface px-4 py-2 text-sm font-medium text-text-primary\"\n >\n Cancel\n </button>\n ) : null}\n </div>\n </section>\n\n <section className=\"space-y-4\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">My codes</h2>\n {codes.length === 0 && !loading ? (\n <EmptyState\n icon={<Ticket className=\"size-8\" />}\n title=\"No affiliate codes yet\"\n description=\"Create your first code to start tracking attributable journeys.\"\n />\n ) : (\n <div className=\"space-y-4\">\n {codes.map((code) => {\n const shareUrl =\n code.destinationUrl ||\n `https://${code.productSlug}.com${code.destinationPath || ''}?ref=${code.code}`;\n return (\n <div\n key={code.id}\n className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\"\n >\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {code.label || code.code}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {code.code} · {code.productSlug}\n </p>\n <p className=\"mt-1 break-all text-xs text-text-secondary\">{shareUrl}</p>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n onClick={() => handleEdit(code)}\n className=\"rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary\"\n >\n Edit\n </button>\n <button\n type=\"button\"\n onClick={() => void handleQuickNote(code.id)}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary\"\n >\n <StickyNote className=\"size-3.5\" />\n Note\n </button>\n {code.status === 'ACTIVE' ? (\n <button\n type=\"button\"\n onClick={() => void handleDeactivate(code.id)}\n className=\"rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-error-text\"\n >\n Deactivate\n </button>\n ) : null}\n </div>\n </div>\n <div className=\"mt-4 grid gap-3 sm:grid-cols-2 xl:grid-cols-4\">\n <div className=\"rounded-md border border-border-subtle bg-bg-surface p-3\">\n <p className=\"text-xs text-text-secondary\">Visits</p>\n <p className=\"text-lg font-semibold text-text-primary\">\n {code.totalVisits}\n </p>\n </div>\n <div className=\"rounded-md border border-border-subtle bg-bg-surface p-3\">\n <p className=\"text-xs text-text-secondary\">Signups</p>\n <p className=\"text-lg font-semibold text-text-primary\">\n {code.totalSignups}\n </p>\n </div>\n <div className=\"rounded-md border border-border-subtle bg-bg-surface p-3\">\n <p className=\"text-xs text-text-secondary\">Paid conversions</p>\n <p className=\"text-lg font-semibold text-text-primary\">\n {code.totalPaidConversions + code.totalStorePurchases}\n </p>\n </div>\n <div className=\"rounded-md border border-border-subtle bg-bg-surface p-3\">\n <p className=\"text-xs text-text-secondary\">Rewards earned</p>\n <p className=\"text-lg font-semibold text-text-primary\">\n {formatCurrency(code.totalRewardAmount, 'USD')}\n </p>\n </div>\n </div>\n {code.notes ? (\n <p className=\"mt-3 text-sm text-text-secondary\">{code.notes}</p>\n ) : null}\n </div>\n );\n })}\n </div>\n )}\n </div>\n\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">\n Recent attributed journeys\n </h2>\n {journeys.length === 0 ? (\n <EmptyState\n icon={<Users className=\"size-8\" />}\n title=\"No journeys yet\"\n description=\"Journeys will appear as visitors land on your affiliate links.\"\n />\n ) : (\n <ResponsiveTable\n data={journeys}\n rowKey={(journey) => journey.id}\n columns={journeyColumns}\n />\n )}\n </div>\n </section>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AffiliateDashboardPage.js","names":[],"sources":["../../../../../src/billing/modules/affiliate/pages/AffiliateDashboardPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState, type FC } from 'react';\nimport { Link2, Plus, RefreshCw, StickyNote, Ticket, TrendingUp, Users } from 'lucide-react';\nimport { ResponsiveTable, type ResponsiveTableColumn } from '@burdenoff/fe-libs/ui';\n\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader, ServerError, StatCard } from '../../../shared/components';\nimport {\n CTAOverflowMenu,\n PagePurpose,\n EmphasisPanel,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { formatCurrency, formatDateTime, isServerError } from '../../../shared/utils';\n\nconst journeyColumns: ResponsiveTableColumn<AffiliateJourney>[] = [\n { key: 'code', header: 'Code', priority: 'primary', cell: (journey) => journey.codeValue },\n { key: 'product', header: 'Product', cell: (journey) => journey.productSlug },\n {\n key: 'path',\n header: 'Path',\n cell: (journey) => journey.currentPath || journey.landingPath || '-',\n },\n { key: 'country', header: 'Country', cell: (journey) => journey.geographyCountry || '-' },\n { key: 'status', header: 'Status', cell: (journey) => journey.status },\n {\n key: 'visited',\n header: 'Visited',\n cell: (journey) => formatDateTime(journey.lastVisitedAt),\n },\n];\nimport {\n addAffiliateCodeNote,\n createAffiliateCode,\n deactivateAffiliateCode,\n fetchMyAffiliateAnalytics,\n fetchMyAffiliateCodes,\n fetchMyAffiliateJourneys,\n updateAffiliateCode,\n type AffiliateCode,\n type AffiliateJourney,\n type AffiliateAnalyticsSummary,\n} from '../api';\n\nexport const AffiliateDashboardPage: FC = () => {\n const permissions = useBillingPermissions();\n const { emit } = useBillingEventEmitter();\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n\n const [codes, setCodes] = useState<AffiliateCode[]>([]);\n const [journeys, setJourneys] = useState<AffiliateJourney[]>([]);\n const [analytics, setAnalytics] = useState<AffiliateAnalyticsSummary | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const [selectedCodeId, setSelectedCodeId] = useState<string | null>(null);\n const [form, setForm] = useState({\n label: '',\n description: '',\n notes: '',\n productSlug: '',\n destinationPath: '',\n destinationUrl: '',\n });\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 const [loadedCodes, loadedAnalytics, loadedJourneys] = await Promise.all([\n fetchMyAffiliateCodes(requestContext),\n fetchMyAffiliateAnalytics(requestContext),\n fetchMyAffiliateJourneys(requestContext),\n ]);\n setCodes(loadedCodes);\n setAnalytics(loadedAnalytics);\n setJourneys(loadedJourneys);\n } catch (loadError) {\n setError(\n loadError instanceof Error ? loadError : new Error('Failed to load affiliate dashboard')\n );\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void loadData();\n }, [requestContext]);\n\n if (!permissions.canViewBillingAccount) {\n return <AccessDenied message=\"You don't have permission to view affiliate analytics.\" />;\n }\n\n if (error && isServerError(error)) {\n return (\n <ServerError\n title=\"Affiliate service unavailable\"\n message={error.message}\n onRetry={() => void loadData()}\n showRetry\n />\n );\n }\n\n const selectedCode = codes.find((item) => item.id === selectedCodeId) ?? null;\n\n const handleSubmit = async () => {\n if (!form.productSlug.trim()) {\n setError(new Error('Product slug is required'));\n return;\n }\n\n try {\n if (selectedCode) {\n await updateAffiliateCode(\n selectedCode.id,\n {\n label: form.label,\n description: form.description,\n notes: form.notes,\n destinationPath: form.destinationPath,\n destinationUrl: form.destinationUrl,\n },\n requestContext\n );\n emit('billing.affiliate_code.updated', {\n route: '/billing/affiliate',\n entityId: selectedCode.id,\n source: 'affiliate-dashboard',\n });\n } else {\n const createdCode = await createAffiliateCode(\n {\n label: form.label,\n description: form.description,\n notes: form.notes,\n productSlug: form.productSlug,\n destinationPath: form.destinationPath,\n destinationUrl: form.destinationUrl,\n },\n requestContext\n );\n emit('billing.affiliate_code.created', {\n route: '/billing/affiliate',\n entityId: createdCode.id,\n source: 'affiliate-dashboard',\n });\n }\n\n setSelectedCodeId(null);\n setForm({\n label: '',\n description: '',\n notes: '',\n productSlug: '',\n destinationPath: '',\n destinationUrl: '',\n });\n await loadData();\n } catch (submitError) {\n setError(\n submitError instanceof Error ? submitError : new Error('Failed to save affiliate code')\n );\n }\n };\n\n const handleEdit = (code: AffiliateCode) => {\n setSelectedCodeId(code.id);\n setForm({\n label: code.label ?? '',\n description: code.description ?? '',\n notes: code.notes ?? '',\n productSlug: code.productSlug,\n destinationPath: code.destinationPath ?? '',\n destinationUrl: code.destinationUrl ?? '',\n });\n };\n\n const handleDeactivate = async (codeId: string) => {\n await deactivateAffiliateCode(codeId, requestContext);\n emit('billing.affiliate_code.deactivated', {\n route: '/billing/affiliate',\n entityId: codeId,\n source: 'affiliate-dashboard',\n });\n await loadData();\n };\n\n const handleQuickNote = async (codeId: string) => {\n const note = window.prompt('Add an internal note for this affiliate code');\n if (!note) return;\n await addAffiliateCodeNote(codeId, note, requestContext);\n emit('billing.affiliate_code.note_added', {\n route: '/billing/affiliate',\n entityId: codeId,\n source: 'affiliate-dashboard',\n });\n await loadData();\n };\n\n const metricCards = analytics\n ? [\n { title: 'Codes', value: analytics.totalCodes, icon: <Ticket className=\"size-5\" /> },\n { title: 'Visits', value: analytics.totalVisits, icon: <Users className=\"size-5\" /> },\n { title: 'Journeys', value: analytics.totalJourneys, icon: <Link2 className=\"size-5\" /> },\n {\n title: 'Revenue influenced',\n value: formatCurrency(analytics.totalRevenueAmount, 'USD'),\n icon: <TrendingUp className=\"size-5\" />,\n },\n ]\n : [];\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title=\"Affiliate codes\"\n description=\"Create referral codes, add internal notes, and monitor attributed journeys.\"\n actions={\n <CTAOverflowMenu\n actions={[\n {\n label: 'Refresh',\n onSelect: () => void loadData(),\n icon: <RefreshCw className=\"size-4\" />,\n },\n ]}\n />\n }\n />\n\n <PagePurpose>\n Generate referral links for any product, share them, and see exactly what they drive —\n visits, signups, paid conversions and the rewards you have earned. Create and manage your\n codes on the left and watch attributed visitor journeys on the right.\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 ) : analytics ? (\n <EmphasisPanel className=\"p-4 sm:p-5\">\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n {metricCards.map((card) => (\n <StatCard key={card.title} label={card.title} value={card.value} icon={card.icon} />\n ))}\n </div>\n </EmphasisPanel>\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 <div className=\"grid gap-6 xl:grid-cols-[1.1fr,1.9fr]\">\n <section className=\"rounded-lg border border-border-seam bg-bg-surface p-4 xl:sticky xl:top-6 xl:self-start\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {selectedCode ? 'Update affiliate code' : 'Create affiliate code'}\n </h2>\n {!selectedCode ? <Plus className=\"size-4 text-text-secondary\" /> : null}\n </div>\n\n <div className=\"space-y-3\">\n <input\n value={form.label}\n onChange={(event) =>\n setForm((current) => ({ ...current, label: event.target.value }))\n }\n placeholder=\"Label\"\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n <input\n value={form.productSlug}\n onChange={(event) =>\n setForm((current) => ({ ...current, productSlug: event.target.value }))\n }\n placeholder=\"Product slug (eg. bigconsole)\"\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n disabled={Boolean(selectedCode)}\n />\n <input\n value={form.destinationPath}\n onChange={(event) =>\n setForm((current) => ({ ...current, destinationPath: event.target.value }))\n }\n placeholder=\"Destination path (eg. /pricing)\"\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n <input\n value={form.destinationUrl}\n onChange={(event) =>\n setForm((current) => ({ ...current, destinationUrl: event.target.value }))\n }\n placeholder=\"Absolute destination URL (optional)\"\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n <textarea\n value={form.description}\n onChange={(event) =>\n setForm((current) => ({ ...current, description: event.target.value }))\n }\n placeholder=\"Description\"\n className=\"min-h-24 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n <textarea\n value={form.notes}\n onChange={(event) =>\n setForm((current) => ({ ...current, notes: event.target.value }))\n }\n placeholder=\"Internal notes\"\n className=\"min-h-24 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </div>\n\n <div className=\"mt-4 flex flex-wrap gap-3\">\n <button\n type=\"button\"\n onClick={() => void handleSubmit()}\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text\"\n >\n {selectedCode ? 'Save changes' : 'Create code'}\n </button>\n {selectedCode ? (\n <button\n type=\"button\"\n onClick={() => {\n setSelectedCodeId(null);\n setForm({\n label: '',\n description: '',\n notes: '',\n productSlug: '',\n destinationPath: '',\n destinationUrl: '',\n });\n }}\n className=\"rounded-md border border-border-subtle bg-bg-surface px-4 py-2 text-sm font-medium text-text-primary\"\n >\n Cancel\n </button>\n ) : null}\n </div>\n </section>\n\n <section className=\"space-y-4\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">My codes</h2>\n {codes.length === 0 && !loading ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No affiliate codes yet\"\n description=\"Create your first code to start tracking attributable journeys.\"\n />\n ) : (\n <div className=\"space-y-4\">\n {codes.map((code) => {\n const shareUrl =\n code.destinationUrl ||\n `https://${code.productSlug}.com${code.destinationPath || ''}?ref=${code.code}`;\n return (\n <div\n key={code.id}\n className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\"\n >\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {code.label || code.code}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {code.code} · {code.productSlug}\n </p>\n <p className=\"mt-1 break-all text-xs text-text-secondary\">{shareUrl}</p>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n onClick={() => handleEdit(code)}\n className=\"rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary\"\n >\n Edit\n </button>\n <button\n type=\"button\"\n onClick={() => void handleQuickNote(code.id)}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary\"\n >\n <StickyNote className=\"size-3.5\" />\n Note\n </button>\n {code.status === 'ACTIVE' ? (\n <button\n type=\"button\"\n onClick={() => void handleDeactivate(code.id)}\n className=\"rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-error-text\"\n >\n Deactivate\n </button>\n ) : null}\n </div>\n </div>\n <div className=\"mt-4 grid gap-3 sm:grid-cols-2 xl:grid-cols-4\">\n <div className=\"rounded-md border border-border-subtle bg-bg-surface p-3\">\n <p className=\"text-xs text-text-secondary\">Visits</p>\n <p className=\"text-lg font-semibold text-text-primary\">\n {code.totalVisits}\n </p>\n </div>\n <div className=\"rounded-md border border-border-subtle bg-bg-surface p-3\">\n <p className=\"text-xs text-text-secondary\">Signups</p>\n <p className=\"text-lg font-semibold text-text-primary\">\n {code.totalSignups}\n </p>\n </div>\n <div className=\"rounded-md border border-border-subtle bg-bg-surface p-3\">\n <p className=\"text-xs text-text-secondary\">Paid conversions</p>\n <p className=\"text-lg font-semibold text-text-primary\">\n {code.totalPaidConversions + code.totalStorePurchases}\n </p>\n </div>\n <div className=\"rounded-md border border-border-subtle bg-bg-surface p-3\">\n <p className=\"text-xs text-text-secondary\">Rewards earned</p>\n <p className=\"text-lg font-semibold text-text-primary\">\n {formatCurrency(code.totalRewardAmount, 'USD')}\n </p>\n </div>\n </div>\n {code.notes ? (\n <p className=\"mt-3 text-sm text-text-secondary\">{code.notes}</p>\n ) : null}\n </div>\n );\n })}\n </div>\n )}\n </div>\n\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">\n Recent attributed journeys\n </h2>\n {journeys.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No journeys yet\"\n description=\"Journeys will appear as visitors land on your affiliate links.\"\n />\n ) : (\n <ResponsiveTable\n data={journeys}\n rowKey={(journey) => journey.id}\n columns={journeyColumns}\n />\n )}\n </div>\n </section>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|