@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.
Files changed (96) hide show
  1. package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js +192 -187
  2. package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js.map +1 -1
  3. package/dist/billing/modules/addons/pages/AddonCreatePage.js +322 -317
  4. package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
  5. package/dist/billing/modules/addons/pages/AddonDetailPage.js +190 -185
  6. package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
  7. package/dist/billing/modules/addons/pages/AddonEditPage.js +297 -292
  8. package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
  9. package/dist/billing/modules/addons/pages/AddonsBrowsePage.js +60 -75
  10. package/dist/billing/modules/addons/pages/AddonsBrowsePage.js.map +1 -1
  11. package/dist/billing/modules/addons/pages/AddonsListPage.js +86 -101
  12. package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
  13. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +124 -121
  14. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
  15. package/dist/billing/modules/analytics/pages/AnalyticsPage.js +103 -111
  16. package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
  17. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +196 -160
  18. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
  19. package/dist/billing/modules/billing/pages/InvoicesListPage.js +58 -73
  20. package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
  21. package/dist/billing/modules/billing/pages/PaymentMethodsPage.js +65 -75
  22. package/dist/billing/modules/billing/pages/PaymentMethodsPage.js.map +1 -1
  23. package/dist/billing/modules/billing/pages/RefundsPage.js +399 -399
  24. package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
  25. package/dist/billing/modules/billing/pages/TransactionsPage.js +49 -48
  26. package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
  27. package/dist/billing/modules/billing/pages/UserRefundsPage.js +144 -151
  28. package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -1
  29. package/dist/billing/modules/checkout/pages/CheckoutPage.js +430 -425
  30. package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
  31. package/dist/billing/modules/coupons/pages/CouponDetailPage.js +121 -93
  32. package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
  33. package/dist/billing/modules/coupons/pages/CouponsListPage.js +154 -152
  34. package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
  35. package/dist/billing/modules/coupons/pages/CreateCouponPage.js +467 -462
  36. package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
  37. package/dist/billing/modules/coupons/pages/EditCouponPage.js +294 -286
  38. package/dist/billing/modules/coupons/pages/EditCouponPage.js.map +1 -1
  39. package/dist/billing/modules/credits/pages/AdminCreditsPage.js +123 -117
  40. package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
  41. package/dist/billing/modules/credits/pages/CreditTransactionsPage.js +43 -42
  42. package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
  43. package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js +192 -207
  44. package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
  45. package/dist/billing/modules/credits/pages/CreditsPage.js +40 -56
  46. package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
  47. package/dist/billing/modules/dashboard/pages/OverviewPage.js +128 -121
  48. package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
  49. package/dist/billing/modules/discounts/pages/DiscountsListPage.js +165 -180
  50. package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
  51. package/dist/billing/modules/earnings/pages/EarningsPage.js +103 -100
  52. package/dist/billing/modules/earnings/pages/EarningsPage.js.map +1 -1
  53. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +175 -175
  54. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
  55. package/dist/billing/modules/earnings/pages/PayoutsPage.js +96 -96
  56. package/dist/billing/modules/earnings/pages/PayoutsPage.js.map +1 -1
  57. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +42 -40
  58. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
  59. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +45 -38
  60. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
  61. package/dist/billing/modules/plans/pages/PlanCreatePage.js +339 -334
  62. package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
  63. package/dist/billing/modules/plans/pages/PlanDetailPage.js +80 -74
  64. package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
  65. package/dist/billing/modules/plans/pages/PlanEditPage.js +342 -337
  66. package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
  67. package/dist/billing/modules/plans/pages/PlansBrowsePage.js +38 -53
  68. package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
  69. package/dist/billing/modules/plans/pages/PlansListPage.js +60 -75
  70. package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
  71. package/dist/billing/modules/promotions/pages/CreatePromotionPage.js +242 -237
  72. package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
  73. package/dist/billing/modules/promotions/pages/PromotionsListPage.js +29 -39
  74. package/dist/billing/modules/promotions/pages/PromotionsListPage.js.map +1 -1
  75. package/dist/billing/modules/quota/pages/QuotaPage.js +1 -0
  76. package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js +299 -294
  77. package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
  78. package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js +191 -186
  79. package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
  80. package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js +308 -303
  81. package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
  82. package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js +57 -78
  83. package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
  84. package/dist/billing/modules/settings/pages/SettingsPage.js +79 -78
  85. package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
  86. package/dist/billing/modules/settings/pages/TeamPermissionsPage.js +15 -13
  87. package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
  88. package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js +338 -299
  89. package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
  90. package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +82 -103
  91. package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
  92. package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js +59 -54
  93. package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
  94. package/dist/billing/modules/usage/pages/UsagePage.js +172 -215
  95. package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
  96. 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 { EmptyState as i } from "../../../shared/components/EmptyState.js";
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 o } from "../../../hooks/useBillingEventEmitter.js";
9
- import { formatCurrency as s, formatDateTime as c } from "../../../shared/utils/format.js";
10
- import { isServerError as l } from "../../../shared/utils/error.js";
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 u } from "../../../hooks/useBillingPermissions.js";
13
- import { addAffiliateCodeNote as d, createAffiliateCode as f, deactivateAffiliateCode as p, fetchMyAffiliateAnalytics as m, fetchMyAffiliateCodes as h, fetchMyAffiliateJourneys as g, updateAffiliateCode as _ } from "../api.js";
14
- import { useEffect as v, useMemo as y, useState as b } from "react";
15
- import { Link2 as x, Plus as S, RefreshCw as C, StickyNote as w, Ticket as T, TrendingUp as E, Users as D } from "lucide-react";
16
- import { jsx as O, jsxs as k } from "react/jsx-runtime";
17
- import { CTAOverflowMenu as A, ResponsiveTable as j } from "@burdenoff/fe-libs/ui";
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 M = [
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) => c(e.lastVisitedAt)
48
+ cell: (e) => s(e.lastVisitedAt)
50
49
  }
51
- ], N = () => {
52
- let c = u(), { emit: N } = o(), { apiGatewayUrl: P, authToken: F, orgId: I } = e(), [L, R] = b([]), [z, B] = b([]), [V, H] = b(null), [U, W] = b(!0), [G, K] = b(null), [q, J] = b(null), [Y, X] = b({
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 = y(() => ({
60
- apiGatewayUrl: P,
61
- authToken: F,
62
- orgId: I
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
- h(Z),
72
- m(Z),
73
- g(Z)
70
+ p(Z),
71
+ f(Z),
72
+ m(Z)
74
73
  ]);
75
- R(e), H(t), B(n);
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 (v(() => {
81
+ if (g(() => {
83
82
  Q();
84
- }, [Z]), !c.canViewBillingAccount) return /* @__PURE__ */ O(t, { message: "You don't have permission to view affiliate analytics." });
85
- if (G && l(G)) return /* @__PURE__ */ O(a, {
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 $ = L.find((e) => e.id === q) ?? null, ee = async () => {
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 _($.id, {
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), N("billing.affiliate_code.updated", {
102
+ }, Z), P("billing.affiliate_code.updated", {
104
103
  route: "/billing/affiliate",
105
104
  entityId: $.id,
106
105
  source: "affiliate-dashboard"
107
- })) : N("billing.affiliate_code.created", {
106
+ })) : P("billing.affiliate_code.created", {
108
107
  route: "/billing/affiliate",
109
- entityId: (await f({
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
- }, te = (e) => {
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
- }, ne = async (e) => {
139
- await p(e, Z), N("billing.affiliate_code.deactivated", {
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
- }, re = async (e) => {
143
+ }, ae = async (e) => {
145
144
  let t = window.prompt("Add an internal note for this affiliate code");
146
- t && (await d(e, t, Z), N("billing.affiliate_code.note_added", {
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
- }, ie = V ? [
150
+ }, oe = H ? [
152
151
  {
153
152
  title: "Codes",
154
- value: V.totalCodes,
155
- icon: /* @__PURE__ */ O(T, { className: "size-5" })
153
+ value: H.totalCodes,
154
+ icon: /* @__PURE__ */ E(C, { className: "size-5" })
156
155
  },
157
156
  {
158
157
  title: "Visits",
159
- value: V.totalVisits,
160
- icon: /* @__PURE__ */ O(D, { className: "size-5" })
158
+ value: H.totalVisits,
159
+ icon: /* @__PURE__ */ E(T, { className: "size-5" })
161
160
  },
162
161
  {
163
162
  title: "Journeys",
164
- value: V.totalJourneys,
165
- icon: /* @__PURE__ */ O(x, { className: "size-5" })
163
+ value: H.totalJourneys,
164
+ icon: /* @__PURE__ */ E(y, { className: "size-5" })
166
165
  },
167
166
  {
168
167
  title: "Revenue influenced",
169
- value: s(V.totalRevenueAmount, "USD"),
170
- icon: /* @__PURE__ */ O(E, { className: "size-5" })
168
+ value: o(H.totalRevenueAmount, "USD"),
169
+ icon: /* @__PURE__ */ E(w, { className: "size-5" })
171
170
  }
172
171
  ] : [];
173
- return /* @__PURE__ */ k("div", {
172
+ return /* @__PURE__ */ D("div", {
174
173
  className: "space-y-6 p-6",
175
174
  children: [
176
- /* @__PURE__ */ O(n, {
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(A, { actions: [{
178
+ actions: /* @__PURE__ */ E(O, { actions: [{
180
179
  label: "Refresh",
181
180
  onSelect: () => void Q(),
182
- icon: /* @__PURE__ */ O(C, { className: "size-4" })
181
+ icon: /* @__PURE__ */ E(x, { className: "size-4" })
183
182
  }] })
184
183
  }),
185
- U ? /* @__PURE__ */ O("div", {
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__ */ O("div", { className: "h-28 animate-pulse rounded-lg border border-border-subtle bg-bg-surface" }, e))
193
- }) : V ? /* @__PURE__ */ O("div", {
194
- className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4",
195
- children: ie.map((e) => /* @__PURE__ */ O(r, {
196
- label: e.title,
197
- value: e.value,
198
- icon: e.icon
199
- }, e.title))
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 && !l(G) ? /* @__PURE__ */ O("div", {
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__ */ k("div", {
208
+ /* @__PURE__ */ D("div", {
206
209
  className: "grid gap-6 xl:grid-cols-[1.1fr,1.9fr]",
207
- children: [/* @__PURE__ */ k("section", {
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__ */ k("div", {
213
+ /* @__PURE__ */ D("div", {
211
214
  className: "mb-4 flex items-center justify-between",
212
- children: [/* @__PURE__ */ O("h2", {
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__ */ O(S, { className: "size-4 text-text-secondary" })]
218
+ }), $ ? null : /* @__PURE__ */ E(b, { className: "size-4 text-text-secondary" })]
216
219
  }),
217
- /* @__PURE__ */ k("div", {
220
+ /* @__PURE__ */ D("div", {
218
221
  className: "space-y-3",
219
222
  children: [
220
- /* @__PURE__ */ O("input", {
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__ */ O("input", {
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__ */ O("input", {
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__ */ O("input", {
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__ */ O("textarea", {
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__ */ O("textarea", {
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__ */ k("div", {
280
+ /* @__PURE__ */ D("div", {
278
281
  className: "mt-4 flex flex-wrap gap-3",
279
- children: [/* @__PURE__ */ O("button", {
282
+ children: [/* @__PURE__ */ E("button", {
280
283
  type: "button",
281
- onClick: () => void ee(),
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__ */ O("button", {
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__ */ k("section", {
304
+ }), /* @__PURE__ */ D("section", {
302
305
  className: "space-y-4",
303
- children: [/* @__PURE__ */ k("div", {
306
+ children: [/* @__PURE__ */ D("div", {
304
307
  className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
305
- children: [/* @__PURE__ */ O("h2", {
308
+ children: [/* @__PURE__ */ E("h2", {
306
309
  className: "mb-4 text-lg font-semibold text-text-primary",
307
310
  children: "My codes"
308
- }), L.length === 0 && !U ? /* @__PURE__ */ O(i, {
309
- icon: /* @__PURE__ */ O(T, { className: "size-8" }),
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__ */ O("div", {
315
+ }) : /* @__PURE__ */ E("div", {
313
316
  className: "space-y-4",
314
- children: L.map((e) => {
317
+ children: R.map((e) => {
315
318
  let t = e.destinationUrl || `https://${e.productSlug}.com${e.destinationPath || ""}?ref=${e.code}`;
316
- return /* @__PURE__ */ k("div", {
319
+ return /* @__PURE__ */ D("div", {
317
320
  className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
318
321
  children: [
319
- /* @__PURE__ */ k("div", {
322
+ /* @__PURE__ */ D("div", {
320
323
  className: "flex flex-wrap items-start justify-between gap-4",
321
- children: [/* @__PURE__ */ k("div", { children: [
322
- /* @__PURE__ */ O("p", {
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__ */ k("p", {
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__ */ O("p", {
337
+ /* @__PURE__ */ E("p", {
335
338
  className: "mt-1 break-all text-xs text-text-secondary",
336
339
  children: t
337
340
  })
338
- ] }), /* @__PURE__ */ k("div", {
341
+ ] }), /* @__PURE__ */ D("div", {
339
342
  className: "flex flex-wrap gap-2",
340
343
  children: [
341
- /* @__PURE__ */ O("button", {
344
+ /* @__PURE__ */ E("button", {
342
345
  type: "button",
343
- onClick: () => te(e),
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__ */ k("button", {
350
+ /* @__PURE__ */ D("button", {
348
351
  type: "button",
349
- onClick: () => void re(e.id),
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__ */ O(w, { className: "size-3.5" }), "Note"]
354
+ children: [/* @__PURE__ */ E(S, { className: "size-3.5" }), "Note"]
352
355
  }),
353
- e.status === "ACTIVE" ? /* @__PURE__ */ O("button", {
356
+ e.status === "ACTIVE" ? /* @__PURE__ */ E("button", {
354
357
  type: "button",
355
- onClick: () => void ne(e.id),
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__ */ k("div", {
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__ */ k("div", {
368
+ /* @__PURE__ */ D("div", {
366
369
  className: "rounded-md border border-border-subtle bg-bg-surface p-3",
367
- children: [/* @__PURE__ */ O("p", {
370
+ children: [/* @__PURE__ */ E("p", {
368
371
  className: "text-xs text-text-secondary",
369
372
  children: "Visits"
370
- }), /* @__PURE__ */ O("p", {
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__ */ k("div", {
378
+ /* @__PURE__ */ D("div", {
376
379
  className: "rounded-md border border-border-subtle bg-bg-surface p-3",
377
- children: [/* @__PURE__ */ O("p", {
380
+ children: [/* @__PURE__ */ E("p", {
378
381
  className: "text-xs text-text-secondary",
379
382
  children: "Signups"
380
- }), /* @__PURE__ */ O("p", {
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__ */ k("div", {
388
+ /* @__PURE__ */ D("div", {
386
389
  className: "rounded-md border border-border-subtle bg-bg-surface p-3",
387
- children: [/* @__PURE__ */ O("p", {
390
+ children: [/* @__PURE__ */ E("p", {
388
391
  className: "text-xs text-text-secondary",
389
392
  children: "Paid conversions"
390
- }), /* @__PURE__ */ O("p", {
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__ */ k("div", {
398
+ /* @__PURE__ */ D("div", {
396
399
  className: "rounded-md border border-border-subtle bg-bg-surface p-3",
397
- children: [/* @__PURE__ */ O("p", {
400
+ children: [/* @__PURE__ */ E("p", {
398
401
  className: "text-xs text-text-secondary",
399
402
  children: "Rewards earned"
400
- }), /* @__PURE__ */ O("p", {
403
+ }), /* @__PURE__ */ E("p", {
401
404
  className: "text-lg font-semibold text-text-primary",
402
- children: s(e.totalRewardAmount, "USD")
405
+ children: o(e.totalRewardAmount, "USD")
403
406
  })]
404
407
  })
405
408
  ]
406
409
  }),
407
- e.notes ? /* @__PURE__ */ O("p", {
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__ */ k("div", {
418
+ }), /* @__PURE__ */ D("div", {
416
419
  className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
417
- children: [/* @__PURE__ */ O("h2", {
420
+ children: [/* @__PURE__ */ E("h2", {
418
421
  className: "mb-4 text-lg font-semibold text-text-primary",
419
422
  children: "Recent attributed journeys"
420
- }), z.length === 0 ? /* @__PURE__ */ O(i, {
421
- icon: /* @__PURE__ */ O(D, { className: "size-8" }),
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__ */ O(j, {
425
- data: z,
427
+ }) : /* @__PURE__ */ E(M, {
428
+ data: B,
426
429
  rowKey: (e) => e.id,
427
- columns: M
430
+ columns: N
428
431
  })]
429
432
  })]
430
433
  })]
@@ -433,6 +436,6 @@ var M = [
433
436
  });
434
437
  };
435
438
  //#endregion
436
- export { N as AffiliateDashboardPage };
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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"}