@burdenoff/microfe-billing 2026.821.1 → 2026.825.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/billing/BillingAdminRoutes.js.map +1 -1
- package/dist/billing/BillingRoot.js.map +1 -1
- package/dist/billing/BillingUserRoutes.js.map +1 -1
- package/dist/billing/hooks/useBillingAccountSelection.js.map +1 -1
- package/dist/billing/hooks/useBillingEventEmitter.js.map +1 -1
- package/dist/billing/hooks/useBillingNavigate.js.map +1 -1
- package/dist/billing/hooks/useBillingPermissions.js.map +1 -1
- package/dist/billing/hooks/useDefaultBillingCurrency.js.map +1 -1
- package/dist/billing/modules/addons/components/FloatingCartDrawer.js.map +1 -1
- package/dist/billing/modules/addons/hooks/useAddons.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsBrowsePage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
- package/dist/billing/modules/addons/store/addonCartStore.js.map +1 -1
- package/dist/billing/modules/affiliate/api.js.map +1 -1
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +107 -107
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js +80 -81
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
- package/dist/billing/modules/billing/hooks/useInvoices.js.map +1 -1
- package/dist/billing/modules/billing/hooks/useRefunds.js.map +1 -1
- package/dist/billing/modules/billing/hooks/useTransactions.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoicesListPage.js +1 -1
- package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/PaymentMethodsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/UserRefundsPage.js +127 -127
- package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -1
- package/dist/billing/modules/checkout/components/BillingAccountSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js.map +1 -1
- package/dist/billing/modules/checkout/components/CreditsAmountInput.js.map +1 -1
- package/dist/billing/modules/checkout/components/NativeCheckoutView.js.map +1 -1
- package/dist/billing/modules/checkout/components/OrderSummary.js.map +1 -1
- package/dist/billing/modules/checkout/components/PromoCodeInput.js.map +1 -1
- package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js.map +1 -1
- package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js.map +1 -1
- package/dist/billing/modules/checkout/components/StoreShippingSection.js.map +1 -1
- package/dist/billing/modules/checkout/components/StripePaymentForm.js.map +1 -1
- package/dist/billing/modules/checkout/components/SubscriptionSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js.map +1 -1
- package/dist/billing/modules/checkout/hooks/useCheckout.js +0 -1
- package/dist/billing/modules/checkout/hooks/useCheckout.js.map +1 -1
- package/dist/billing/modules/checkout/hooks/useNativeCheckout.js.map +1 -1
- package/dist/billing/modules/checkout/hooks/usePaymentProviders.js.map +1 -1
- package/dist/billing/modules/checkout/hooks/usePaymentStatusPolling.js.map +1 -1
- package/dist/billing/modules/checkout/hooks/useStoreShipping.js.map +1 -1
- package/dist/billing/modules/checkout/pages/CheckoutPage.js +412 -395
- package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
- package/dist/billing/modules/checkout/utils/storeOrder.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/EditCouponPage.js.map +1 -1
- package/dist/billing/modules/credits/hooks/useCredits.js.map +1 -1
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
- package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js.map +1 -1
- package/dist/billing/modules/dashboard/components/BillingAccountCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js.map +1 -1
- package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/hooks/useBillingOverviewData.js.map +1 -1
- package/dist/billing/modules/dashboard/hooks/useDashboard.js.map +1 -1
- package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
- package/dist/billing/modules/earnings/api.js.map +1 -1
- package/dist/billing/modules/earnings/pages/DeveloperRevenueAdminPage.js +145 -145
- package/dist/billing/modules/earnings/pages/DeveloperRevenueAdminPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/EarningsPage.js +133 -133
- package/dist/billing/modules/earnings/pages/EarningsPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +7 -9
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutsPage.js +95 -95
- package/dist/billing/modules/earnings/pages/PayoutsPage.js.map +1 -1
- package/dist/billing/modules/plans/constants/featuredQuotas.js.map +1 -1
- package/dist/billing/modules/plans/hooks/usePlans.js.map +1 -1
- package/dist/billing/modules/plans/hooks/useQuotas.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/PromotionsListPage.js.map +1 -1
- package/dist/billing/modules/quota/hooks/useQuotaTemplates.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaAssignFreePage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
- package/dist/billing/modules/store/pages/StoreCheckoutPage.js +5 -6
- package/dist/billing/modules/store/pages/StoreCheckoutPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js.map +1 -1
- package/dist/billing/modules/subscriptions/hooks/useSubscriptions.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
- package/dist/billing/modules/usage/hooks/useUsage.js +14 -9
- package/dist/billing/modules/usage/hooks/useUsage.js.map +1 -1
- package/dist/billing/modules/usage/pages/UsagePage.js +481 -453
- package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
- package/dist/billing/providers/BillingProvider.js.map +1 -1
- package/dist/billing/shared/components/AccessDenied.js.map +1 -1
- package/dist/billing/shared/components/ActorIdentity.js.map +1 -1
- package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js.map +1 -1
- package/dist/billing/shared/components/EmptyState.js.map +1 -1
- package/dist/billing/shared/components/PageHeader.js.map +1 -1
- package/dist/billing/shared/components/ServerError.js.map +1 -1
- package/dist/billing/shared/components/StatCard.js.map +1 -1
- package/dist/billing/shared/hooks/useActorProfiles.js.map +1 -1
- package/dist/billing/shared/types/graphql.js.map +1 -1
- package/dist/billing/shared/ui/Card.js.map +1 -1
- package/dist/billing/shared/ui/MetricCard.js.map +1 -1
- package/dist/billing/shared/ui/ProgressBar.js.map +1 -1
- package/dist/billing/shared/ui/StatusPill.js.map +1 -1
- package/dist/billing/shared/utils/directBillingGraphql.js.map +1 -1
- package/dist/billing/shared/utils/error.js.map +1 -1
- package/dist/billing/shared/utils/format.js.map +1 -1
- package/dist/billing/shared/utils/gatewayGraphql.js.map +1 -1
- package/dist/billing/shared/utils/navigation.js.map +1 -1
- package/dist/billing/shared/utils/paymentTheme.js.map +1 -1
- package/dist/billing/shared/utils/planFeatureQuantity.js.map +1 -1
- package/dist/billing/shared/utils/status.js.map +1 -1
- package/dist/billing/shared/utils/tokens.js.map +1 -1
- package/dist/generated/global-operations.js +5 -1
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/global-types.js.map +1 -1
- package/dist/generated/wspace-operations.js +1 -1
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/node_modules/@capacitor/core/dist/index.js +1 -1
- package/dist/node_modules/@capacitor/core/dist/index.js.map +1 -1
- package/package.json +13 -10
|
@@ -9,13 +9,13 @@ import { formatCurrency as o, formatDateTime as s } from "../../../shared/utils/
|
|
|
9
9
|
import { isServerError as c } from "../../../shared/utils/error.js";
|
|
10
10
|
import "../../../shared/utils/index.js";
|
|
11
11
|
import { useBillingPermissions as l } from "../../../hooks/useBillingPermissions.js";
|
|
12
|
-
import { addAffiliateCodeNote as
|
|
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
|
|
15
|
-
import { jsx as
|
|
16
|
-
import { CTAOverflowMenu as
|
|
12
|
+
import { addAffiliateCodeNote as u, createAffiliateCode as d, deactivateAffiliateCode as f, fetchMyAffiliateAnalytics as p, fetchMyAffiliateCodes as ee, fetchMyAffiliateJourneys as te, updateAffiliateCode as m } from "../api.js";
|
|
13
|
+
import { useCallback as h, 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 ne, TrendingUp as C, Users as w } from "lucide-react";
|
|
15
|
+
import { jsx as T, jsxs as E } from "react/jsx-runtime";
|
|
16
|
+
import { CTAOverflowMenu as D, EmphasisPanel as O, IllustratedEmptyState as k, PagePurpose as A, ResponsiveTable as j } from "@burdenoff/fe-libs/ui";
|
|
17
17
|
//#region src/billing/modules/affiliate/pages/AffiliateDashboardPage.tsx
|
|
18
|
-
var
|
|
18
|
+
var M = [
|
|
19
19
|
{
|
|
20
20
|
key: "code",
|
|
21
21
|
header: "Code",
|
|
@@ -47,8 +47,8 @@ var N = [
|
|
|
47
47
|
header: "Visited",
|
|
48
48
|
cell: (e) => s(e.lastVisitedAt)
|
|
49
49
|
}
|
|
50
|
-
],
|
|
51
|
-
let s = l(), { emit:
|
|
50
|
+
], N = () => {
|
|
51
|
+
let s = l(), { emit: N } = a(), { apiGatewayUrl: P, authToken: F, orgId: I } = e(), [L, R] = v([]), [z, B] = v([]), [V, H] = v(null), [U, W] = v(!0), [G, K] = v(null), [q, J] = v(null), [Y, X] = v({
|
|
52
52
|
label: "",
|
|
53
53
|
description: "",
|
|
54
54
|
notes: "",
|
|
@@ -56,56 +56,56 @@ var N = [
|
|
|
56
56
|
destinationPath: "",
|
|
57
57
|
destinationUrl: ""
|
|
58
58
|
}), Z = _(() => ({
|
|
59
|
-
apiGatewayUrl:
|
|
60
|
-
authToken:
|
|
61
|
-
orgId:
|
|
59
|
+
apiGatewayUrl: P,
|
|
60
|
+
authToken: F,
|
|
61
|
+
orgId: I
|
|
62
62
|
}), [
|
|
63
|
+
P,
|
|
63
64
|
F,
|
|
64
|
-
I
|
|
65
|
-
|
|
66
|
-
]), Q = async () => {
|
|
65
|
+
I
|
|
66
|
+
]), Q = h(async () => {
|
|
67
67
|
W(!0), K(null);
|
|
68
68
|
try {
|
|
69
69
|
let [e, t, n] = await Promise.all([
|
|
70
|
+
ee(Z),
|
|
70
71
|
p(Z),
|
|
71
|
-
|
|
72
|
-
m(Z)
|
|
72
|
+
te(Z)
|
|
73
73
|
]);
|
|
74
|
-
|
|
74
|
+
R(e), H(t), B(n);
|
|
75
75
|
} catch (e) {
|
|
76
76
|
K(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to load affiliate dashboard"));
|
|
77
77
|
} finally {
|
|
78
78
|
W(!1);
|
|
79
79
|
}
|
|
80
|
-
};
|
|
80
|
+
}, [Z]);
|
|
81
81
|
if (g(() => {
|
|
82
82
|
Q();
|
|
83
|
-
}, [
|
|
84
|
-
if (G && c(G)) return /* @__PURE__ */
|
|
83
|
+
}, [Q]), !s.canViewBillingAccount) return /* @__PURE__ */ T(t, { message: "You don't have permission to view affiliate analytics." });
|
|
84
|
+
if (G && c(G)) return /* @__PURE__ */ T(i, {
|
|
85
85
|
title: "Affiliate service unavailable",
|
|
86
86
|
message: G.message,
|
|
87
87
|
onRetry: () => void Q(),
|
|
88
88
|
showRetry: !0
|
|
89
89
|
});
|
|
90
|
-
let $ =
|
|
90
|
+
let $ = L.find((e) => e.id === q) ?? null, re = async () => {
|
|
91
91
|
if (!Y.productSlug.trim()) {
|
|
92
92
|
K(/* @__PURE__ */ Error("Product slug is required"));
|
|
93
93
|
return;
|
|
94
94
|
}
|
|
95
95
|
try {
|
|
96
|
-
$ ? (await
|
|
96
|
+
$ ? (await m($.id, {
|
|
97
97
|
label: Y.label,
|
|
98
98
|
description: Y.description,
|
|
99
99
|
notes: Y.notes,
|
|
100
100
|
destinationPath: Y.destinationPath,
|
|
101
101
|
destinationUrl: Y.destinationUrl
|
|
102
|
-
}, Z),
|
|
102
|
+
}, Z), N("billing.affiliate_code.updated", {
|
|
103
103
|
route: "/billing/affiliate",
|
|
104
104
|
entityId: $.id,
|
|
105
105
|
source: "affiliate-dashboard"
|
|
106
|
-
})) :
|
|
106
|
+
})) : N("billing.affiliate_code.created", {
|
|
107
107
|
route: "/billing/affiliate",
|
|
108
|
-
entityId: (await
|
|
108
|
+
entityId: (await d({
|
|
109
109
|
label: Y.label,
|
|
110
110
|
description: Y.description,
|
|
111
111
|
notes: Y.notes,
|
|
@@ -125,7 +125,7 @@ var N = [
|
|
|
125
125
|
} catch (e) {
|
|
126
126
|
K(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to save affiliate code"));
|
|
127
127
|
}
|
|
128
|
-
},
|
|
128
|
+
}, ie = (e) => {
|
|
129
129
|
J(e.id), X({
|
|
130
130
|
label: e.label ?? "",
|
|
131
131
|
description: e.description ?? "",
|
|
@@ -134,93 +134,93 @@ var N = [
|
|
|
134
134
|
destinationPath: e.destinationPath ?? "",
|
|
135
135
|
destinationUrl: e.destinationUrl ?? ""
|
|
136
136
|
});
|
|
137
|
-
},
|
|
138
|
-
await
|
|
137
|
+
}, ae = async (e) => {
|
|
138
|
+
await f(e, Z), N("billing.affiliate_code.deactivated", {
|
|
139
139
|
route: "/billing/affiliate",
|
|
140
140
|
entityId: e,
|
|
141
141
|
source: "affiliate-dashboard"
|
|
142
142
|
}), await Q();
|
|
143
|
-
},
|
|
143
|
+
}, oe = async (e) => {
|
|
144
144
|
let t = window.prompt("Add an internal note for this affiliate code");
|
|
145
|
-
t && (await
|
|
145
|
+
t && (await u(e, t, Z), N("billing.affiliate_code.note_added", {
|
|
146
146
|
route: "/billing/affiliate",
|
|
147
147
|
entityId: e,
|
|
148
148
|
source: "affiliate-dashboard"
|
|
149
149
|
}), await Q());
|
|
150
|
-
},
|
|
150
|
+
}, se = V ? [
|
|
151
151
|
{
|
|
152
152
|
title: "Codes",
|
|
153
|
-
value:
|
|
154
|
-
icon: /* @__PURE__ */
|
|
153
|
+
value: V.totalCodes,
|
|
154
|
+
icon: /* @__PURE__ */ T(ne, { className: "size-5" })
|
|
155
155
|
},
|
|
156
156
|
{
|
|
157
157
|
title: "Visits",
|
|
158
|
-
value:
|
|
159
|
-
icon: /* @__PURE__ */
|
|
158
|
+
value: V.totalVisits,
|
|
159
|
+
icon: /* @__PURE__ */ T(w, { className: "size-5" })
|
|
160
160
|
},
|
|
161
161
|
{
|
|
162
162
|
title: "Journeys",
|
|
163
|
-
value:
|
|
164
|
-
icon: /* @__PURE__ */
|
|
163
|
+
value: V.totalJourneys,
|
|
164
|
+
icon: /* @__PURE__ */ T(y, { className: "size-5" })
|
|
165
165
|
},
|
|
166
166
|
{
|
|
167
167
|
title: "Revenue influenced",
|
|
168
|
-
value: o(
|
|
169
|
-
icon: /* @__PURE__ */
|
|
168
|
+
value: o(V.totalRevenueAmount, "USD"),
|
|
169
|
+
icon: /* @__PURE__ */ T(C, { className: "size-5" })
|
|
170
170
|
}
|
|
171
171
|
] : [];
|
|
172
|
-
return /* @__PURE__ */
|
|
172
|
+
return /* @__PURE__ */ E("div", {
|
|
173
173
|
className: "space-y-6 p-6",
|
|
174
174
|
children: [
|
|
175
|
-
/* @__PURE__ */
|
|
175
|
+
/* @__PURE__ */ T(n, {
|
|
176
176
|
title: "Affiliate codes",
|
|
177
177
|
description: "Create referral codes, add internal notes, and monitor attributed journeys.",
|
|
178
|
-
actions: /* @__PURE__ */
|
|
178
|
+
actions: /* @__PURE__ */ T(D, { actions: [{
|
|
179
179
|
label: "Refresh",
|
|
180
180
|
onSelect: () => void Q(),
|
|
181
|
-
icon: /* @__PURE__ */
|
|
181
|
+
icon: /* @__PURE__ */ T(x, { className: "size-4" })
|
|
182
182
|
}] })
|
|
183
183
|
}),
|
|
184
|
-
/* @__PURE__ */
|
|
185
|
-
U ? /* @__PURE__ */
|
|
184
|
+
/* @__PURE__ */ T(A, { 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__ */ T("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
|
-
}) :
|
|
192
|
+
].map((e) => /* @__PURE__ */ T("div", { className: "h-28 animate-pulse rounded-lg border border-border-subtle bg-bg-surface" }, e))
|
|
193
|
+
}) : V ? /* @__PURE__ */ T(O, {
|
|
194
194
|
className: "p-4 sm:p-5",
|
|
195
|
-
children: /* @__PURE__ */
|
|
195
|
+
children: /* @__PURE__ */ T("div", {
|
|
196
196
|
className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4",
|
|
197
|
-
children:
|
|
197
|
+
children: se.map((e) => /* @__PURE__ */ T(r, {
|
|
198
198
|
label: e.title,
|
|
199
199
|
value: e.value,
|
|
200
200
|
icon: e.icon
|
|
201
201
|
}, e.title))
|
|
202
202
|
})
|
|
203
203
|
}) : null,
|
|
204
|
-
G && !c(G) ? /* @__PURE__ */
|
|
204
|
+
G && !c(G) ? /* @__PURE__ */ T("div", {
|
|
205
205
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4 text-sm text-status-error-text",
|
|
206
206
|
children: G.message
|
|
207
207
|
}) : null,
|
|
208
|
-
/* @__PURE__ */
|
|
208
|
+
/* @__PURE__ */ E("div", {
|
|
209
209
|
className: "grid gap-6 xl:grid-cols-[1.1fr,1.9fr]",
|
|
210
|
-
children: [/* @__PURE__ */
|
|
210
|
+
children: [/* @__PURE__ */ E("section", {
|
|
211
211
|
className: "rounded-lg border border-border-seam bg-bg-surface p-4 xl:sticky xl:top-6 xl:self-start",
|
|
212
212
|
children: [
|
|
213
|
-
/* @__PURE__ */
|
|
213
|
+
/* @__PURE__ */ E("div", {
|
|
214
214
|
className: "mb-4 flex items-center justify-between",
|
|
215
|
-
children: [/* @__PURE__ */
|
|
215
|
+
children: [/* @__PURE__ */ T("h2", {
|
|
216
216
|
className: "text-lg font-semibold text-text-primary",
|
|
217
217
|
children: $ ? "Update affiliate code" : "Create affiliate code"
|
|
218
|
-
}), $ ? null : /* @__PURE__ */
|
|
218
|
+
}), $ ? null : /* @__PURE__ */ T(b, { className: "size-4 text-text-secondary" })]
|
|
219
219
|
}),
|
|
220
|
-
/* @__PURE__ */
|
|
220
|
+
/* @__PURE__ */ E("div", {
|
|
221
221
|
className: "space-y-3",
|
|
222
222
|
children: [
|
|
223
|
-
/* @__PURE__ */
|
|
223
|
+
/* @__PURE__ */ T("input", {
|
|
224
224
|
value: Y.label,
|
|
225
225
|
onChange: (e) => X((t) => ({
|
|
226
226
|
...t,
|
|
@@ -229,7 +229,7 @@ var N = [
|
|
|
229
229
|
placeholder: "Label",
|
|
230
230
|
className: "w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary"
|
|
231
231
|
}),
|
|
232
|
-
/* @__PURE__ */
|
|
232
|
+
/* @__PURE__ */ T("input", {
|
|
233
233
|
value: Y.productSlug,
|
|
234
234
|
onChange: (e) => X((t) => ({
|
|
235
235
|
...t,
|
|
@@ -239,7 +239,7 @@ var N = [
|
|
|
239
239
|
className: "w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary",
|
|
240
240
|
disabled: !!$
|
|
241
241
|
}),
|
|
242
|
-
/* @__PURE__ */
|
|
242
|
+
/* @__PURE__ */ T("input", {
|
|
243
243
|
value: Y.destinationPath,
|
|
244
244
|
onChange: (e) => X((t) => ({
|
|
245
245
|
...t,
|
|
@@ -248,7 +248,7 @@ var N = [
|
|
|
248
248
|
placeholder: "Destination path (eg. /pricing)",
|
|
249
249
|
className: "w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary"
|
|
250
250
|
}),
|
|
251
|
-
/* @__PURE__ */
|
|
251
|
+
/* @__PURE__ */ T("input", {
|
|
252
252
|
value: Y.destinationUrl,
|
|
253
253
|
onChange: (e) => X((t) => ({
|
|
254
254
|
...t,
|
|
@@ -257,7 +257,7 @@ var N = [
|
|
|
257
257
|
placeholder: "Absolute destination URL (optional)",
|
|
258
258
|
className: "w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary"
|
|
259
259
|
}),
|
|
260
|
-
/* @__PURE__ */
|
|
260
|
+
/* @__PURE__ */ T("textarea", {
|
|
261
261
|
value: Y.description,
|
|
262
262
|
onChange: (e) => X((t) => ({
|
|
263
263
|
...t,
|
|
@@ -266,7 +266,7 @@ var N = [
|
|
|
266
266
|
placeholder: "Description",
|
|
267
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"
|
|
268
268
|
}),
|
|
269
|
-
/* @__PURE__ */
|
|
269
|
+
/* @__PURE__ */ T("textarea", {
|
|
270
270
|
value: Y.notes,
|
|
271
271
|
onChange: (e) => X((t) => ({
|
|
272
272
|
...t,
|
|
@@ -277,14 +277,14 @@ var N = [
|
|
|
277
277
|
})
|
|
278
278
|
]
|
|
279
279
|
}),
|
|
280
|
-
/* @__PURE__ */
|
|
280
|
+
/* @__PURE__ */ E("div", {
|
|
281
281
|
className: "mt-4 flex flex-wrap gap-3",
|
|
282
|
-
children: [/* @__PURE__ */
|
|
282
|
+
children: [/* @__PURE__ */ T("button", {
|
|
283
283
|
type: "button",
|
|
284
|
-
onClick: () => void
|
|
284
|
+
onClick: () => void re(),
|
|
285
285
|
className: "rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text",
|
|
286
286
|
children: $ ? "Save changes" : "Create code"
|
|
287
|
-
}), $ ? /* @__PURE__ */
|
|
287
|
+
}), $ ? /* @__PURE__ */ T("button", {
|
|
288
288
|
type: "button",
|
|
289
289
|
onClick: () => {
|
|
290
290
|
J(null), X({
|
|
@@ -301,32 +301,32 @@ var N = [
|
|
|
301
301
|
}) : null]
|
|
302
302
|
})
|
|
303
303
|
]
|
|
304
|
-
}), /* @__PURE__ */
|
|
304
|
+
}), /* @__PURE__ */ E("section", {
|
|
305
305
|
className: "space-y-4",
|
|
306
|
-
children: [/* @__PURE__ */
|
|
306
|
+
children: [/* @__PURE__ */ E("div", {
|
|
307
307
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
308
|
-
children: [/* @__PURE__ */
|
|
308
|
+
children: [/* @__PURE__ */ T("h2", {
|
|
309
309
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
310
310
|
children: "My codes"
|
|
311
|
-
}),
|
|
311
|
+
}), L.length === 0 && !U ? /* @__PURE__ */ T(k, {
|
|
312
312
|
illustration: "empty-data",
|
|
313
313
|
title: "No affiliate codes yet",
|
|
314
314
|
description: "Create your first code to start tracking attributable journeys."
|
|
315
|
-
}) : /* @__PURE__ */
|
|
315
|
+
}) : /* @__PURE__ */ T("div", {
|
|
316
316
|
className: "space-y-4",
|
|
317
|
-
children:
|
|
317
|
+
children: L.map((e) => {
|
|
318
318
|
let t = e.destinationUrl || `https://${e.productSlug}.com${e.destinationPath || ""}?ref=${e.code}`;
|
|
319
|
-
return /* @__PURE__ */
|
|
319
|
+
return /* @__PURE__ */ E("div", {
|
|
320
320
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
321
321
|
children: [
|
|
322
|
-
/* @__PURE__ */
|
|
322
|
+
/* @__PURE__ */ E("div", {
|
|
323
323
|
className: "flex flex-wrap items-start justify-between gap-4",
|
|
324
|
-
children: [/* @__PURE__ */
|
|
325
|
-
/* @__PURE__ */
|
|
324
|
+
children: [/* @__PURE__ */ E("div", { children: [
|
|
325
|
+
/* @__PURE__ */ T("p", {
|
|
326
326
|
className: "text-sm font-semibold text-text-primary",
|
|
327
327
|
children: e.label || e.code
|
|
328
328
|
}),
|
|
329
|
-
/* @__PURE__ */
|
|
329
|
+
/* @__PURE__ */ E("p", {
|
|
330
330
|
className: "text-xs text-text-secondary",
|
|
331
331
|
children: [
|
|
332
332
|
e.code,
|
|
@@ -334,80 +334,80 @@ var N = [
|
|
|
334
334
|
e.productSlug
|
|
335
335
|
]
|
|
336
336
|
}),
|
|
337
|
-
/* @__PURE__ */
|
|
337
|
+
/* @__PURE__ */ T("p", {
|
|
338
338
|
className: "mt-1 break-all text-xs text-text-secondary",
|
|
339
339
|
children: t
|
|
340
340
|
})
|
|
341
|
-
] }), /* @__PURE__ */
|
|
341
|
+
] }), /* @__PURE__ */ E("div", {
|
|
342
342
|
className: "flex flex-wrap gap-2",
|
|
343
343
|
children: [
|
|
344
|
-
/* @__PURE__ */
|
|
344
|
+
/* @__PURE__ */ T("button", {
|
|
345
345
|
type: "button",
|
|
346
|
-
onClick: () =>
|
|
346
|
+
onClick: () => ie(e),
|
|
347
347
|
className: "rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary",
|
|
348
348
|
children: "Edit"
|
|
349
349
|
}),
|
|
350
|
-
/* @__PURE__ */
|
|
350
|
+
/* @__PURE__ */ E("button", {
|
|
351
351
|
type: "button",
|
|
352
|
-
onClick: () => void
|
|
352
|
+
onClick: () => void oe(e.id),
|
|
353
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",
|
|
354
|
-
children: [/* @__PURE__ */
|
|
354
|
+
children: [/* @__PURE__ */ T(S, { className: "size-3.5" }), "Note"]
|
|
355
355
|
}),
|
|
356
|
-
e.status === "ACTIVE" ? /* @__PURE__ */
|
|
356
|
+
e.status === "ACTIVE" ? /* @__PURE__ */ T("button", {
|
|
357
357
|
type: "button",
|
|
358
|
-
onClick: () => void
|
|
358
|
+
onClick: () => void ae(e.id),
|
|
359
359
|
className: "rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-error-text",
|
|
360
360
|
children: "Deactivate"
|
|
361
361
|
}) : null
|
|
362
362
|
]
|
|
363
363
|
})]
|
|
364
364
|
}),
|
|
365
|
-
/* @__PURE__ */
|
|
365
|
+
/* @__PURE__ */ E("div", {
|
|
366
366
|
className: "mt-4 grid gap-3 sm:grid-cols-2 xl:grid-cols-4",
|
|
367
367
|
children: [
|
|
368
|
-
/* @__PURE__ */
|
|
368
|
+
/* @__PURE__ */ E("div", {
|
|
369
369
|
className: "rounded-md border border-border-subtle bg-bg-surface p-3",
|
|
370
|
-
children: [/* @__PURE__ */
|
|
370
|
+
children: [/* @__PURE__ */ T("p", {
|
|
371
371
|
className: "text-xs text-text-secondary",
|
|
372
372
|
children: "Visits"
|
|
373
|
-
}), /* @__PURE__ */
|
|
373
|
+
}), /* @__PURE__ */ T("p", {
|
|
374
374
|
className: "text-lg font-semibold text-text-primary",
|
|
375
375
|
children: e.totalVisits
|
|
376
376
|
})]
|
|
377
377
|
}),
|
|
378
|
-
/* @__PURE__ */
|
|
378
|
+
/* @__PURE__ */ E("div", {
|
|
379
379
|
className: "rounded-md border border-border-subtle bg-bg-surface p-3",
|
|
380
|
-
children: [/* @__PURE__ */
|
|
380
|
+
children: [/* @__PURE__ */ T("p", {
|
|
381
381
|
className: "text-xs text-text-secondary",
|
|
382
382
|
children: "Signups"
|
|
383
|
-
}), /* @__PURE__ */
|
|
383
|
+
}), /* @__PURE__ */ T("p", {
|
|
384
384
|
className: "text-lg font-semibold text-text-primary",
|
|
385
385
|
children: e.totalSignups
|
|
386
386
|
})]
|
|
387
387
|
}),
|
|
388
|
-
/* @__PURE__ */
|
|
388
|
+
/* @__PURE__ */ E("div", {
|
|
389
389
|
className: "rounded-md border border-border-subtle bg-bg-surface p-3",
|
|
390
|
-
children: [/* @__PURE__ */
|
|
390
|
+
children: [/* @__PURE__ */ T("p", {
|
|
391
391
|
className: "text-xs text-text-secondary",
|
|
392
392
|
children: "Paid conversions"
|
|
393
|
-
}), /* @__PURE__ */
|
|
393
|
+
}), /* @__PURE__ */ T("p", {
|
|
394
394
|
className: "text-lg font-semibold text-text-primary",
|
|
395
395
|
children: e.totalPaidConversions + e.totalStorePurchases
|
|
396
396
|
})]
|
|
397
397
|
}),
|
|
398
|
-
/* @__PURE__ */
|
|
398
|
+
/* @__PURE__ */ E("div", {
|
|
399
399
|
className: "rounded-md border border-border-subtle bg-bg-surface p-3",
|
|
400
|
-
children: [/* @__PURE__ */
|
|
400
|
+
children: [/* @__PURE__ */ T("p", {
|
|
401
401
|
className: "text-xs text-text-secondary",
|
|
402
402
|
children: "Rewards earned"
|
|
403
|
-
}), /* @__PURE__ */
|
|
403
|
+
}), /* @__PURE__ */ T("p", {
|
|
404
404
|
className: "text-lg font-semibold text-text-primary",
|
|
405
405
|
children: o(e.totalRewardAmount, "USD")
|
|
406
406
|
})]
|
|
407
407
|
})
|
|
408
408
|
]
|
|
409
409
|
}),
|
|
410
|
-
e.notes ? /* @__PURE__ */
|
|
410
|
+
e.notes ? /* @__PURE__ */ T("p", {
|
|
411
411
|
className: "mt-3 text-sm text-text-secondary",
|
|
412
412
|
children: e.notes
|
|
413
413
|
}) : null
|
|
@@ -415,19 +415,19 @@ var N = [
|
|
|
415
415
|
}, e.id);
|
|
416
416
|
})
|
|
417
417
|
})]
|
|
418
|
-
}), /* @__PURE__ */
|
|
418
|
+
}), /* @__PURE__ */ E("div", {
|
|
419
419
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
420
|
-
children: [/* @__PURE__ */
|
|
420
|
+
children: [/* @__PURE__ */ T("h2", {
|
|
421
421
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
422
422
|
children: "Recent attributed journeys"
|
|
423
|
-
}),
|
|
423
|
+
}), z.length === 0 ? /* @__PURE__ */ T(k, {
|
|
424
424
|
illustration: "empty-people",
|
|
425
425
|
title: "No journeys yet",
|
|
426
426
|
description: "Journeys will appear as visitors land on your affiliate links."
|
|
427
|
-
}) : /* @__PURE__ */
|
|
428
|
-
data:
|
|
427
|
+
}) : /* @__PURE__ */ T(j, {
|
|
428
|
+
data: z,
|
|
429
429
|
rowKey: (e) => e.id,
|
|
430
|
-
columns:
|
|
430
|
+
columns: M
|
|
431
431
|
})]
|
|
432
432
|
})]
|
|
433
433
|
})]
|
|
@@ -436,6 +436,6 @@ var N = [
|
|
|
436
436
|
});
|
|
437
437
|
};
|
|
438
438
|
//#endregion
|
|
439
|
-
export {
|
|
439
|
+
export { N as AffiliateDashboardPage };
|
|
440
440
|
|
|
441
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 { 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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"}
|
|
1
|
+
{"version":3,"file":"AffiliateDashboardPage.js","names":[],"sources":["../../../../../src/billing/modules/affiliate/pages/AffiliateDashboardPage.tsx"],"sourcesContent":["import { useCallback, 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 = useCallback(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 }, [requestContext]);\n\n useEffect(() => {\n void loadData();\n }, [loadData]);\n\n if (!permissions.canViewBillingAccount) {\n return <AccessDenied message=\"You don't have permission to view 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":";;;;;;;;;;;;;;;;;AAgBA,IAAM,IAA4D;CAChE;EAAE,KAAK;EAAQ,QAAQ;EAAQ,UAAU;EAAW,OAAO,MAAY,EAAQ;CAAU;CACzF;EAAE,KAAK;EAAW,QAAQ;EAAW,OAAO,MAAY,EAAQ;CAAY;CAC5E;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MAAY,EAAQ,eAAe,EAAQ,eAAe;CACnE;CACA;EAAE,KAAK;EAAW,QAAQ;EAAW,OAAO,MAAY,EAAQ,oBAAoB;CAAI;CACxF;EAAE,KAAK;EAAU,QAAQ;EAAU,OAAO,MAAY,EAAQ;CAAO;CACrE;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MAAY,EAAe,EAAQ,aAAa;CACzD;AACF,GAca,UAAmC;CAC9C,IAAM,IAAc,EAAsB,GACpC,EAAE,YAAS,EAAuB,GAClC,EAAE,kBAAe,cAAW,aAAU,EAAW,GAEjD,CAAC,GAAO,KAAY,EAA0B,CAAC,CAAC,GAChD,CAAC,GAAU,KAAe,EAA6B,CAAC,CAAC,GACzD,CAAC,GAAW,KAAgB,EAA2C,IAAI,GAC3E,CAAC,GAAS,KAAc,EAAS,EAAI,GACrC,CAAC,GAAO,KAAY,EAAuB,IAAI,GAC/C,CAAC,GAAgB,KAAqB,EAAwB,IAAI,GAClE,CAAC,GAAM,KAAW,EAAS;EAC/B,OAAO;EACP,aAAa;EACb,OAAO;EACP,aAAa;EACb,iBAAiB;EACjB,gBAAgB;CAClB,CAAC,GAEK,IAAiB,SACd;EACL;EACA;EACA;CACF,IACA;EAAC;EAAe;EAAW;CAAK,CAClC,GAEM,IAAW,EAAY,YAAY;EAEvC,AADA,EAAW,EAAI,GACf,EAAS,IAAI;EACb,IAAI;GACF,IAAM,CAAC,GAAa,GAAiB,KAAkB,MAAM,QAAQ,IAAI;IACvE,GAAsB,CAAc;IACpC,EAA0B,CAAc;IACxC,GAAyB,CAAc;GACzC,CAAC;GAGD,AAFA,EAAS,CAAW,GACpB,EAAa,CAAe,GAC5B,EAAY,CAAc;EAC5B,SAAS,GAAW;GAClB,EACE,aAAqB,QAAQ,IAAY,gBAAI,MAAM,oCAAoC,CACzF;EACF,UAAU;GACR,EAAW,EAAK;EAClB;CACF,GAAG,CAAC,CAAc,CAAC;CAMnB,IAJA,QAAgB;EACd,EAAc;CAChB,GAAG,CAAC,CAAQ,CAAC,GAET,CAAC,EAAY,uBACf,OAAO,kBAAC,GAAD,EAAc,SAAQ,yDAA0D,CAAA;CAGzF,IAAI,KAAS,EAAc,CAAK,GAC9B,OACE,kBAAC,GAAD;EACE,OAAM;EACN,SAAS,EAAM;EACf,eAAe,KAAK,EAAS;EAC7B,WAAA;CACD,CAAA;CAIL,IAAM,IAAe,EAAM,MAAM,MAAS,EAAK,OAAO,CAAc,KAAK,MAEnE,KAAe,YAAY;EAC/B,IAAI,CAAC,EAAK,YAAY,KAAK,GAAG;GAC5B,EAAS,gBAAI,MAAM,0BAA0B,CAAC;GAC9C;EACF;EAEA,IAAI;GA8CF,AA7CI,KACF,MAAM,EACJ,EAAa,IACb;IACE,OAAO,EAAK;IACZ,aAAa,EAAK;IAClB,OAAO,EAAK;IACZ,iBAAiB,EAAK;IACtB,gBAAgB,EAAK;GACvB,GACA,CACF,GACA,EAAK,kCAAkC;IACrC,OAAO;IACP,UAAU,EAAa;IACvB,QAAQ;GACV,CAAC,KAaD,EAAK,kCAAkC;IACrC,OAAO;IACP,WAAU,MAbc,EACxB;KACE,OAAO,EAAK;KACZ,aAAa,EAAK;KAClB,OAAO,EAAK;KACZ,aAAa,EAAK;KAClB,iBAAiB,EAAK;KACtB,gBAAgB,EAAK;IACvB,GACA,CACF,GAGwB;IACtB,QAAQ;GACV,CAAC,GAGH,EAAkB,IAAI,GACtB,EAAQ;IACN,OAAO;IACP,aAAa;IACb,OAAO;IACP,aAAa;IACb,iBAAiB;IACjB,gBAAgB;GAClB,CAAC,GACD,MAAM,EAAS;EACjB,SAAS,GAAa;GACpB,EACE,aAAuB,QAAQ,IAAc,gBAAI,MAAM,+BAA+B,CACxF;EACF;CACF,GAEM,MAAc,MAAwB;EAE1C,AADA,EAAkB,EAAK,EAAE,GACzB,EAAQ;GACN,OAAO,EAAK,SAAS;GACrB,aAAa,EAAK,eAAe;GACjC,OAAO,EAAK,SAAS;GACrB,aAAa,EAAK;GAClB,iBAAiB,EAAK,mBAAmB;GACzC,gBAAgB,EAAK,kBAAkB;EACzC,CAAC;CACH,GAEM,KAAmB,OAAO,MAAmB;EAOjD,AANA,MAAM,EAAwB,GAAQ,CAAc,GACpD,EAAK,sCAAsC;GACzC,OAAO;GACP,UAAU;GACV,QAAQ;EACV,CAAC,GACD,MAAM,EAAS;CACjB,GAEM,KAAkB,OAAO,MAAmB;EAChD,IAAM,IAAO,OAAO,OAAO,8CAA8C;EACpE,MACL,MAAM,EAAqB,GAAQ,GAAM,CAAc,GACvD,EAAK,qCAAqC;GACxC,OAAO;GACP,UAAU;GACV,QAAQ;EACV,CAAC,GACD,MAAM,EAAS;CACjB,GAEM,KAAc,IAChB;EACE;GAAE,OAAO;GAAS,OAAO,EAAU;GAAY,MAAM,kBAAC,IAAD,EAAQ,WAAU,SAAU,CAAA;EAAE;EACnF;GAAE,OAAO;GAAU,OAAO,EAAU;GAAa,MAAM,kBAAC,GAAD,EAAO,WAAU,SAAU,CAAA;EAAE;EACpF;GAAE,OAAO;GAAY,OAAO,EAAU;GAAe,MAAM,kBAAC,GAAD,EAAO,WAAU,SAAU,CAAA;EAAE;EACxF;GACE,OAAO;GACP,OAAO,EAAe,EAAU,oBAAoB,KAAK;GACzD,MAAM,kBAAC,GAAD,EAAY,WAAU,SAAU,CAAA;EACxC;CACF,IACA,CAAC;CAEL,OACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD;IACE,OAAM;IACN,aAAY;IACZ,SACE,kBAAC,GAAD,EACE,SAAS,CACP;KACE,OAAO;KACP,gBAAgB,KAAK,EAAS;KAC9B,MAAM,kBAAC,GAAD,EAAW,WAAU,SAAU,CAAA;IACvC,CACF,EACD,CAAA;GAEJ,CAAA;GAED,kBAAC,GAAD,EAAA,UAAa,yPAIA,CAAA;GAEZ,IACC,kBAAC,OAAD;IAAK,WAAU;cACZ;KAAC;KAAG;KAAG;KAAG;IAAC,EAAE,KAAK,MACjB,kBAAC,OAAD,EAEE,WAAU,0EACX,GAFM,CAEN,CACF;GACE,CAAA,IACH,IACF,kBAAC,GAAD;IAAe,WAAU;cACvB,kBAAC,OAAD;KAAK,WAAU;eACZ,GAAY,KAAK,MAChB,kBAAC,GAAD;MAA2B,OAAO,EAAK;MAAO,OAAO,EAAK;MAAO,MAAM,EAAK;KAAO,GAApE,EAAK,KAA+D,CACpF;IACE,CAAA;GACQ,CAAA,IACb;GAEH,KAAS,CAAC,EAAc,CAAK,IAC5B,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAM;GACJ,CAAA,IACH;GAEJ,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,WAAD;KAAS,WAAU;eAAnB;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBACX,IAAe,0BAA0B;OACxC,CAAA,GACF,IAAiE,OAAlD,kBAAC,GAAD,EAAM,WAAU,6BAA8B,CAAA,CAC5D;;MAEL,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,SAAD;SACE,OAAO,EAAK;SACZ,WAAW,MACT,GAAS,OAAa;UAAE,GAAG;UAAS,OAAO,EAAM,OAAO;SAAM,EAAE;SAElE,aAAY;SACZ,WAAU;QACX,CAAA;QACD,kBAAC,SAAD;SACE,OAAO,EAAK;SACZ,WAAW,MACT,GAAS,OAAa;UAAE,GAAG;UAAS,aAAa,EAAM,OAAO;SAAM,EAAE;SAExE,aAAY;SACZ,WAAU;SACV,UAAU,EAAQ;QACnB,CAAA;QACD,kBAAC,SAAD;SACE,OAAO,EAAK;SACZ,WAAW,MACT,GAAS,OAAa;UAAE,GAAG;UAAS,iBAAiB,EAAM,OAAO;SAAM,EAAE;SAE5E,aAAY;SACZ,WAAU;QACX,CAAA;QACD,kBAAC,SAAD;SACE,OAAO,EAAK;SACZ,WAAW,MACT,GAAS,OAAa;UAAE,GAAG;UAAS,gBAAgB,EAAM,OAAO;SAAM,EAAE;SAE3E,aAAY;SACZ,WAAU;QACX,CAAA;QACD,kBAAC,YAAD;SACE,OAAO,EAAK;SACZ,WAAW,MACT,GAAS,OAAa;UAAE,GAAG;UAAS,aAAa,EAAM,OAAO;SAAM,EAAE;SAExE,aAAY;SACZ,WAAU;QACX,CAAA;QACD,kBAAC,YAAD;SACE,OAAO,EAAK;SACZ,WAAW,MACT,GAAS,OAAa;UAAE,GAAG;UAAS,OAAO,EAAM,OAAO;SAAM,EAAE;SAElE,aAAY;SACZ,WAAU;QACX,CAAA;OACE;;MAEL,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,KAAK,GAAa;QACjC,WAAU;kBAET,IAAe,iBAAiB;OAC3B,CAAA,GACP,IACC,kBAAC,UAAD;QACE,MAAK;QACL,eAAe;SAEb,AADA,EAAkB,IAAI,GACtB,EAAQ;UACN,OAAO;UACP,aAAa;UACb,OAAO;UACP,aAAa;UACb,iBAAiB;UACjB,gBAAgB;SAClB,CAAC;QACH;QACA,WAAU;kBACX;OAEO,CAAA,IACN,IACD;;KACE;QAET,kBAAC,WAAD;KAAS,WAAU;eAAnB,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA+C;MAAY,CAAA,GACxE,EAAM,WAAW,KAAK,CAAC,IACtB,kBAAC,GAAD;OACE,cAAa;OACb,OAAM;OACN,aAAY;MACb,CAAA,IAED,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAM,KAAK,MAAS;QACnB,IAAM,IACJ,EAAK,kBACL,WAAW,EAAK,YAAY,MAAM,EAAK,mBAAmB,GAAG,OAAO,EAAK;QAC3E,OACE,kBAAC,OAAD;SAEE,WAAU;mBAFZ;UAIE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,OAAD,EAAA,UAAA;YACE,kBAAC,KAAD;aAAG,WAAU;uBACV,EAAK,SAAS,EAAK;YACnB,CAAA;YACH,kBAAC,KAAD;aAAG,WAAU;uBAAb;cACG,EAAK;cAAK;cAAI,EAAK;aACnB;;YACH,kBAAC,KAAD;aAAG,WAAU;uBAA8C;YAAY,CAAA;WACpE,EAAA,CAAA,GACL,kBAAC,OAAD;YAAK,WAAU;sBAAf;aACE,kBAAC,UAAD;cACE,MAAK;cACL,eAAe,GAAW,CAAI;cAC9B,WAAU;wBACX;aAEO,CAAA;aACR,kBAAC,UAAD;cACE,MAAK;cACL,eAAe,KAAK,GAAgB,EAAK,EAAE;cAC3C,WAAU;wBAHZ,CAKE,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA,GAAC,MAE7B;;aACP,EAAK,WAAW,WACf,kBAAC,UAAD;cACE,MAAK;cACL,eAAe,KAAK,GAAiB,EAAK,EAAE;cAC5C,WAAU;wBACX;aAEO,CAAA,IACN;YACD;aACF;;UACL,kBAAC,OAAD;WAAK,WAAU;qBAAf;YACE,kBAAC,OAAD;aAAK,WAAU;uBAAf,CACE,kBAAC,KAAD;cAAG,WAAU;wBAA8B;aAAS,CAAA,GACpD,kBAAC,KAAD;cAAG,WAAU;wBACV,EAAK;aACL,CAAA,CACA;;YACL,kBAAC,OAAD;aAAK,WAAU;uBAAf,CACE,kBAAC,KAAD;cAAG,WAAU;wBAA8B;aAAU,CAAA,GACrD,kBAAC,KAAD;cAAG,WAAU;wBACV,EAAK;aACL,CAAA,CACA;;YACL,kBAAC,OAAD;aAAK,WAAU;uBAAf,CACE,kBAAC,KAAD;cAAG,WAAU;wBAA8B;aAAmB,CAAA,GAC9D,kBAAC,KAAD;cAAG,WAAU;wBACV,EAAK,uBAAuB,EAAK;aACjC,CAAA,CACA;;YACL,kBAAC,OAAD;aAAK,WAAU;uBAAf,CACE,kBAAC,KAAD;cAAG,WAAU;wBAA8B;aAAiB,CAAA,GAC5D,kBAAC,KAAD;cAAG,WAAU;wBACV,EAAe,EAAK,mBAAmB,KAAK;aAC5C,CAAA,CACA;;WACF;;UACJ,EAAK,QACJ,kBAAC,KAAD;WAAG,WAAU;qBAAoC,EAAK;UAAS,CAAA,IAC7D;SACD;WArEE,EAAK,EAqEP;OAET,CAAC;MACE,CAAA,CAEJ;SAEL,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA+C;MAEzD,CAAA,GACH,EAAS,WAAW,IACnB,kBAAC,GAAD;OACE,cAAa;OACb,OAAM;OACN,aAAY;MACb,CAAA,IAED,kBAAC,GAAD;OACE,MAAM;OACN,SAAS,MAAY,EAAQ;OAC7B,SAAS;MACV,CAAA,CAEA;OACE;MACN;;EACF;;AAET"}
|