@burdenoff/microfe-billing 2026.625.3 → 2026.625.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/billing/lib/native-purchases.js +2 -44
- package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js +192 -187
- package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonCreatePage.js +322 -317
- package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonDetailPage.js +190 -185
- package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonEditPage.js +297 -292
- package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsBrowsePage.js +60 -75
- package/dist/billing/modules/addons/pages/AddonsBrowsePage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsListPage.js +86 -101
- package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +124 -121
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js +103 -111
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +196 -160
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoicesListPage.js +58 -73
- package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/PaymentMethodsPage.js +65 -75
- package/dist/billing/modules/billing/pages/PaymentMethodsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/RefundsPage.js +399 -399
- package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/TransactionsPage.js +49 -48
- package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/UserRefundsPage.js +144 -151
- package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -1
- package/dist/billing/modules/checkout/pages/CheckoutPage.js +430 -425
- package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js +121 -93
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponsListPage.js +154 -152
- package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js +467 -462
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/EditCouponPage.js +294 -286
- package/dist/billing/modules/coupons/pages/EditCouponPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js +123 -117
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js +43 -42
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js +192 -207
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditsPage.js +40 -56
- package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
- package/dist/billing/modules/dashboard/pages/OverviewPage.js +128 -121
- package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js +165 -180
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/EarningsPage.js +103 -100
- package/dist/billing/modules/earnings/pages/EarningsPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +175 -175
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutsPage.js +96 -96
- package/dist/billing/modules/earnings/pages/PayoutsPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +42 -40
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +45 -38
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanCreatePage.js +339 -334
- package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanDetailPage.js +80 -74
- package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanEditPage.js +342 -337
- package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js +38 -53
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansListPage.js +60 -75
- package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js +242 -237
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/PromotionsListPage.js +29 -39
- package/dist/billing/modules/promotions/pages/PromotionsListPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaPage.js +1 -0
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js +299 -294
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js +191 -186
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js +308 -303
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js +57 -78
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/SettingsPage.js +79 -78
- package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/TeamPermissionsPage.js +15 -13
- package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js +338 -299
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +82 -103
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js +59 -54
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
- package/dist/billing/modules/usage/pages/UsagePage.js +172 -215
- package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
- package/package.json +2 -2
- package/dist/billing/lib/native-purchases.js.map +0 -1
|
@@ -12,8 +12,9 @@ import { AlertCircle as f, ArrowLeft as p, ArrowRight as m, CheckCircle as h, Ch
|
|
|
12
12
|
import { useParams as S } from "react-router-dom";
|
|
13
13
|
import { Fragment as C, jsx as w, jsxs as T } from "react/jsx-runtime";
|
|
14
14
|
import { useI18n as E } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
15
|
+
import { PagePurpose as D } from "@burdenoff/fe-libs/ui";
|
|
15
16
|
//#region src/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.tsx
|
|
16
|
-
var
|
|
17
|
+
var O = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ? e === -1 || e === Infinity ? "Unlimited" : e >= 1e6 ? `${(e / 1e6).toFixed(1)}M` : e >= 1e3 ? `${(e / 1e3).toFixed(1)}K` : e.toLocaleString() : String(e), k = (e) => e.replace(/_/g, " ").replace(/([A-Z])/g, " $1").replace(/^./, (e) => e.toUpperCase()).trim(), A = (e) => {
|
|
17
18
|
let t = /* @__PURE__ */ new Map();
|
|
18
19
|
for (let n of e) {
|
|
19
20
|
let e = n.quotaId || n.quota?.id;
|
|
@@ -38,8 +39,8 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
38
39
|
});
|
|
39
40
|
}
|
|
40
41
|
return Array.from(t.values());
|
|
41
|
-
},
|
|
42
|
-
let i = u(() =>
|
|
42
|
+
}, j = ({ features: e, isExpanded: t, onToggle: n, variant: r = "available" }) => {
|
|
43
|
+
let i = u(() => A(e), [e]), a = i.length;
|
|
43
44
|
return a === 0 ? /* @__PURE__ */ w("p", {
|
|
44
45
|
className: "text-xs text-text-secondary italic",
|
|
45
46
|
children: "No features defined"
|
|
@@ -79,13 +80,13 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
79
80
|
className: "flex items-center gap-1 text-xs text-text-secondary",
|
|
80
81
|
children: [/* @__PURE__ */ T("span", {
|
|
81
82
|
className: "opacity-70",
|
|
82
|
-
children: [
|
|
83
|
+
children: [k(e), ":"]
|
|
83
84
|
}), /* @__PURE__ */ w("span", {
|
|
84
85
|
className: "font-medium text-text-primary flex items-center gap-0.5",
|
|
85
86
|
children: t === -1 || t === Infinity ? /* @__PURE__ */ T(C, { children: [/* @__PURE__ */ w(v, { className: "size-3" }), /* @__PURE__ */ w("span", {
|
|
86
87
|
className: "sr-only",
|
|
87
88
|
children: "Unlimited"
|
|
88
|
-
})] }) :
|
|
89
|
+
})] }) : O(t)
|
|
89
90
|
})]
|
|
90
91
|
}, e))
|
|
91
92
|
}),
|
|
@@ -98,36 +99,36 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
98
99
|
})
|
|
99
100
|
})]
|
|
100
101
|
});
|
|
101
|
-
},
|
|
102
|
+
}, M = () => {
|
|
102
103
|
let { t: g } = E(), _ = (e, t) => {
|
|
103
104
|
let n = g(e);
|
|
104
105
|
return n === e ? t : n;
|
|
105
|
-
}, { subscriptionId: v } = S(), x = s(), { orgId: C } = e(),
|
|
106
|
+
}, { subscriptionId: v } = S(), x = s(), { orgId: C } = e(), O = i(), { selectedAccountId: k } = t({
|
|
106
107
|
orgId: C,
|
|
107
108
|
syncFromUrl: !0,
|
|
108
109
|
urlParamName: "billingAccountId"
|
|
109
|
-
}), { data:
|
|
110
|
-
let e =
|
|
111
|
-
return
|
|
112
|
-
}, [
|
|
113
|
-
let e =
|
|
114
|
-
return
|
|
115
|
-
}, [
|
|
116
|
-
|
|
110
|
+
}), { data: M } = o({ fetchPolicy: "cache-first" }), N = k ?? M?.getDefaultBillingAccount?.id ?? void 0, { subscription: P, isLoading: F } = l(v, N), { data: I, loading: L } = a(), [R, z] = d(null), [B, V] = d({}), H = u(() => I?.getAllPlans || [], [I]), U = u(() => {
|
|
111
|
+
let e = P?.planId;
|
|
112
|
+
return H.find((t) => t.id === e) || null;
|
|
113
|
+
}, [H, P?.planId]), W = u(() => {
|
|
114
|
+
let e = P?.planId, t = P?.plan?.productID;
|
|
115
|
+
return H.filter((n) => n.id === e || !n.isActive ? !1 : t ? n.productID === t : !0).sort((e, t) => e.price - t.price);
|
|
116
|
+
}, [H, P]), G = u(() => W.find((e) => e.id === R) || null, [W, R]), K = G !== null && G.price < (P?.plan?.price ?? 0), q = (e) => {
|
|
117
|
+
V((t) => ({
|
|
117
118
|
...t,
|
|
118
119
|
[e]: !t[e]
|
|
119
120
|
}));
|
|
120
|
-
},
|
|
121
|
-
if (!
|
|
122
|
-
let e =
|
|
123
|
-
x(`/checkout/${
|
|
121
|
+
}, J = () => {
|
|
122
|
+
if (!P || !G) return;
|
|
123
|
+
let e = P.plan?.price ?? 0, t = G.price < e;
|
|
124
|
+
x(`/checkout/${G.id}`, { state: {
|
|
124
125
|
purchaseType: "PLAN",
|
|
125
|
-
...t ? { downgradeFromSubscriptionId:
|
|
126
|
-
currentPlanName:
|
|
127
|
-
billingAccountId:
|
|
126
|
+
...t ? { downgradeFromSubscriptionId: P.id } : { upgradeFromSubscriptionId: P.id },
|
|
127
|
+
currentPlanName: P.plan?.name,
|
|
128
|
+
billingAccountId: P.billingAccountId
|
|
128
129
|
} });
|
|
129
130
|
};
|
|
130
|
-
if (!
|
|
131
|
+
if (!O.canUpdateSubscription) return /* @__PURE__ */ w("div", {
|
|
131
132
|
className: "flex items-center justify-center h-full min-h-[400px]",
|
|
132
133
|
children: /* @__PURE__ */ T("div", {
|
|
133
134
|
className: "text-center space-y-2",
|
|
@@ -147,7 +148,7 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
147
148
|
]
|
|
148
149
|
})
|
|
149
150
|
});
|
|
150
|
-
if (
|
|
151
|
+
if (F || L) return /* @__PURE__ */ w("div", {
|
|
151
152
|
className: "flex items-center justify-center min-h-[400px]",
|
|
152
153
|
children: /* @__PURE__ */ T("div", {
|
|
153
154
|
className: "text-center space-y-4",
|
|
@@ -157,7 +158,7 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
157
158
|
})]
|
|
158
159
|
})
|
|
159
160
|
});
|
|
160
|
-
if (!
|
|
161
|
+
if (!P) return /* @__PURE__ */ w("div", {
|
|
161
162
|
className: "flex items-center justify-center h-full min-h-[400px]",
|
|
162
163
|
children: /* @__PURE__ */ T("div", {
|
|
163
164
|
className: "text-center space-y-4",
|
|
@@ -169,18 +170,18 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
169
170
|
}),
|
|
170
171
|
/* @__PURE__ */ w("button", {
|
|
171
172
|
type: "button",
|
|
172
|
-
onClick: () => x(c("/subscriptions",
|
|
173
|
+
onClick: () => x(c("/subscriptions", N)),
|
|
173
174
|
className: "px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button",
|
|
174
175
|
children: _("billing.subscriptions.backToSubscriptions", "Back to Subscriptions")
|
|
175
176
|
})
|
|
176
177
|
]
|
|
177
178
|
})
|
|
178
179
|
});
|
|
179
|
-
if (
|
|
180
|
+
if (P.status !== "active") return /* @__PURE__ */ T("div", {
|
|
180
181
|
className: "max-w-2xl mx-auto p-6 space-y-6",
|
|
181
182
|
children: [/* @__PURE__ */ T("button", {
|
|
182
183
|
type: "button",
|
|
183
|
-
onClick: () => x(c(`/subscriptions/${v}`,
|
|
184
|
+
onClick: () => x(c(`/subscriptions/${v}`, N)),
|
|
184
185
|
className: "flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary",
|
|
185
186
|
children: [/* @__PURE__ */ w(p, { className: "size-4" }), _("billing.subscriptions.backToSubscription", "Back to Subscription")]
|
|
186
187
|
}), /* @__PURE__ */ T("div", {
|
|
@@ -197,18 +198,18 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
197
198
|
}),
|
|
198
199
|
/* @__PURE__ */ w("button", {
|
|
199
200
|
type: "button",
|
|
200
|
-
onClick: () => x(c("/subscriptions",
|
|
201
|
+
onClick: () => x(c("/subscriptions", N)),
|
|
201
202
|
className: "px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button",
|
|
202
203
|
children: _("billing.subscriptions.viewAll", "View All Subscriptions")
|
|
203
204
|
})
|
|
204
205
|
]
|
|
205
206
|
})]
|
|
206
207
|
});
|
|
207
|
-
if (
|
|
208
|
+
if (W.length === 0) return /* @__PURE__ */ T("div", {
|
|
208
209
|
className: "max-w-2xl mx-auto p-6 space-y-6",
|
|
209
210
|
children: [/* @__PURE__ */ T("button", {
|
|
210
211
|
type: "button",
|
|
211
|
-
onClick: () => x(c(`/subscriptions/${v}`,
|
|
212
|
+
onClick: () => x(c(`/subscriptions/${v}`, N)),
|
|
212
213
|
className: "flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary",
|
|
213
214
|
children: [/* @__PURE__ */ w(p, { className: "size-4" }), _("billing.subscriptions.backToSubscription", "Back to Subscription")]
|
|
214
215
|
}), /* @__PURE__ */ T("div", {
|
|
@@ -226,14 +227,14 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
226
227
|
]
|
|
227
228
|
})]
|
|
228
229
|
});
|
|
229
|
-
let
|
|
230
|
+
let Y = U?.features || [];
|
|
230
231
|
return /* @__PURE__ */ T("div", {
|
|
231
232
|
className: "max-w-3xl mx-auto p-6 space-y-6",
|
|
232
233
|
children: [
|
|
233
234
|
/* @__PURE__ */ T("div", { children: [
|
|
234
235
|
/* @__PURE__ */ T("button", {
|
|
235
236
|
type: "button",
|
|
236
|
-
onClick: () => x(c(`/subscriptions/${v}`,
|
|
237
|
+
onClick: () => x(c(`/subscriptions/${v}`, N)),
|
|
237
238
|
className: "flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary mb-4",
|
|
238
239
|
children: [/* @__PURE__ */ w(p, { className: "size-4" }), _("billing.subscriptions.backToSubscription", "Back to Subscription")]
|
|
239
240
|
}),
|
|
@@ -247,10 +248,14 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
247
248
|
"Switch from ",
|
|
248
249
|
/* @__PURE__ */ w("span", {
|
|
249
250
|
className: "font-medium",
|
|
250
|
-
children:
|
|
251
|
+
children: P.plan?.name
|
|
251
252
|
}),
|
|
252
253
|
" to a different plan"
|
|
253
254
|
]
|
|
255
|
+
}),
|
|
256
|
+
/* @__PURE__ */ w(D, {
|
|
257
|
+
className: "mt-3",
|
|
258
|
+
children: _("billing.subscriptions.upgradePurpose", "Move this subscription to a different plan — upgrade for more capacity or downgrade to save. Compare what each plan includes and see how the change affects your billing before you confirm.")
|
|
254
259
|
})
|
|
255
260
|
] }),
|
|
256
261
|
/* @__PURE__ */ w("div", {
|
|
@@ -266,12 +271,12 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
266
271
|
}),
|
|
267
272
|
/* @__PURE__ */ w("p", {
|
|
268
273
|
className: "font-semibold text-text-primary",
|
|
269
|
-
children:
|
|
274
|
+
children: P.plan?.name
|
|
270
275
|
}),
|
|
271
|
-
/* @__PURE__ */ w(
|
|
272
|
-
features:
|
|
273
|
-
isExpanded:
|
|
274
|
-
onToggle: () =>
|
|
276
|
+
/* @__PURE__ */ w(j, {
|
|
277
|
+
features: Y,
|
|
278
|
+
isExpanded: B.current || !1,
|
|
279
|
+
onToggle: () => q("current"),
|
|
275
280
|
variant: "current"
|
|
276
281
|
})
|
|
277
282
|
]
|
|
@@ -282,12 +287,12 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
282
287
|
children: "Price"
|
|
283
288
|
}), /* @__PURE__ */ T("p", {
|
|
284
289
|
className: "font-semibold text-text-primary",
|
|
285
|
-
children: [n(
|
|
290
|
+
children: [n(P.plan?.price || 0, P.plan?.currency || "USD"), /* @__PURE__ */ T("span", {
|
|
286
291
|
className: "text-sm font-normal text-text-secondary",
|
|
287
292
|
children: [
|
|
288
293
|
" ",
|
|
289
294
|
"/ ",
|
|
290
|
-
r(
|
|
295
|
+
r(P.plan?.duration || "monthly")
|
|
291
296
|
]
|
|
292
297
|
})]
|
|
293
298
|
})]
|
|
@@ -301,13 +306,13 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
301
306
|
children: "Select New Plan"
|
|
302
307
|
}), /* @__PURE__ */ w("div", {
|
|
303
308
|
className: "grid gap-3",
|
|
304
|
-
children:
|
|
305
|
-
let t = e.price - (
|
|
309
|
+
children: W.map((e) => {
|
|
310
|
+
let t = e.price - (P.plan?.price || 0), i = R === e.id, a = e.features || [], o = B[e.id] || !1;
|
|
306
311
|
return /* @__PURE__ */ T("div", {
|
|
307
312
|
className: `rounded-lg border transition-all ${i ? "border-border-strong bg-[var(--color-accent-soft)] ring-2 ring-[var(--color-focus-ring)]" : "border-border-subtle bg-bg-surface hover:border-border-strong"}`,
|
|
308
313
|
children: [/* @__PURE__ */ w("button", {
|
|
309
314
|
type: "button",
|
|
310
|
-
onClick: () =>
|
|
315
|
+
onClick: () => z(e.id),
|
|
311
316
|
className: "w-full p-4 text-left",
|
|
312
317
|
children: /* @__PURE__ */ T("div", {
|
|
313
318
|
className: "flex items-start justify-between",
|
|
@@ -345,10 +350,10 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
345
350
|
})
|
|
346
351
|
}), /* @__PURE__ */ w("div", {
|
|
347
352
|
className: "px-4 pb-4 pt-0 ml-8",
|
|
348
|
-
children: /* @__PURE__ */ w(
|
|
353
|
+
children: /* @__PURE__ */ w(j, {
|
|
349
354
|
features: a,
|
|
350
355
|
isExpanded: o,
|
|
351
|
-
onToggle: () =>
|
|
356
|
+
onToggle: () => q(e.id),
|
|
352
357
|
variant: "available"
|
|
353
358
|
})
|
|
354
359
|
})]
|
|
@@ -356,8 +361,8 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
356
361
|
})
|
|
357
362
|
})]
|
|
358
363
|
}),
|
|
359
|
-
|
|
360
|
-
let e =
|
|
364
|
+
G && (() => {
|
|
365
|
+
let e = A(Y).length, t = A(G.features || []).length;
|
|
361
366
|
return /* @__PURE__ */ T("div", {
|
|
362
367
|
className: "bg-bg-surface border border-border-subtle rounded-lg p-4 space-y-4",
|
|
363
368
|
children: [
|
|
@@ -373,7 +378,7 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
373
378
|
}),
|
|
374
379
|
/* @__PURE__ */ w("p", {
|
|
375
380
|
className: "font-medium text-text-primary",
|
|
376
|
-
children:
|
|
381
|
+
children: P.plan?.name
|
|
377
382
|
}),
|
|
378
383
|
/* @__PURE__ */ T("p", {
|
|
379
384
|
className: "text-xs text-text-secondary",
|
|
@@ -395,7 +400,7 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
395
400
|
}),
|
|
396
401
|
/* @__PURE__ */ w("p", {
|
|
397
402
|
className: "font-medium text-text-link",
|
|
398
|
-
children:
|
|
403
|
+
children: G.name
|
|
399
404
|
}),
|
|
400
405
|
/* @__PURE__ */ T("p", {
|
|
401
406
|
className: "text-xs text-text-secondary",
|
|
@@ -417,14 +422,14 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
417
422
|
className: "flex items-center justify-end gap-3 pt-2 border-t border-border-subtle",
|
|
418
423
|
children: [/* @__PURE__ */ w("button", {
|
|
419
424
|
type: "button",
|
|
420
|
-
onClick: () => x(c(`/subscriptions/${v}`,
|
|
425
|
+
onClick: () => x(c(`/subscriptions/${v}`, N)),
|
|
421
426
|
className: "px-4 py-2 text-sm font-medium text-text-secondary hover:text-text-primary",
|
|
422
427
|
children: "Cancel"
|
|
423
428
|
}), /* @__PURE__ */ w("button", {
|
|
424
429
|
type: "button",
|
|
425
|
-
onClick:
|
|
430
|
+
onClick: J,
|
|
426
431
|
className: "px-6 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors",
|
|
427
|
-
children:
|
|
432
|
+
children: K ? "Downgrade Plan" : "Upgrade Plan"
|
|
428
433
|
})]
|
|
429
434
|
})
|
|
430
435
|
]
|
|
@@ -434,6 +439,6 @@ var D = (e) => typeof e == "boolean" ? e ? "Yes" : "No" : typeof e == "number" ?
|
|
|
434
439
|
});
|
|
435
440
|
};
|
|
436
441
|
//#endregion
|
|
437
|
-
export {
|
|
442
|
+
export { M as UpgradeSubscriptionPage };
|
|
438
443
|
|
|
439
444
|
//# sourceMappingURL=UpgradeSubscriptionPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UpgradeSubscriptionPage.js","names":[],"sources":["../../../../../src/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.tsx"],"sourcesContent":["/**\n * Change Plan Page\n * Allows users to change their current subscription to a different plan\n * Shows plan features in collapsible sections for easy comparison\n */\n\nimport { type FC, useState, useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useBillingAccountSelection } from '../../../hooks/useBillingAccountSelection';\nimport {\n ArrowLeft,\n ArrowRight,\n Loader2,\n Package,\n CheckCircle,\n AlertCircle,\n ChevronDown,\n ChevronUp,\n Sparkles,\n Infinity as InfinityIcon,\n} from 'lucide-react';\n\nimport { useSubscription } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { formatCurrency, formatPlanDuration } from '../../../shared/utils/format';\nimport {\n useGetAllPlansQuery,\n useGetDefaultBillingAccountDashboardQuery,\n} from '../../../../generated/global-operations';\nimport type { Plan, PlanDuration, SubscriptionStatus } from '../../../shared/types';\nimport { withBillingAccountId } from '../../../shared/utils/navigation';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\n// Helper type for plan features\ninterface PlanFeature {\n id: string;\n quotaId?: string;\n quota?: {\n id: string;\n name: string;\n limits?: Record<string, number | string | boolean>;\n reusable?: boolean;\n quotaType?: string;\n };\n}\n\n// Helper function to format limit values nicely\nconst formatLimitValue = (value: number | string | boolean): string => {\n if (typeof value === 'boolean') {\n return value ? 'Yes' : 'No';\n }\n if (typeof value === 'number') {\n if (value === -1 || value === Infinity) return 'Unlimited';\n if (value >= 1000000) return `${(value / 1000000).toFixed(1)}M`;\n if (value >= 1000) return `${(value / 1000).toFixed(1)}K`;\n return value.toLocaleString();\n }\n return String(value);\n};\n\n// Helper function to get a user-friendly limit key name\nconst formatLimitKey = (key: string): string => {\n return key\n .replace(/_/g, ' ')\n .replace(/([A-Z])/g, ' $1')\n .replace(/^./, (str) => str.toUpperCase())\n .trim();\n};\n\n// Helper function to aggregate features by quotaId\n// Combines limits for features with the same quota\nconst aggregateFeaturesByQuota = (features: PlanFeature[]): PlanFeature[] => {\n const quotaMap = new Map<string, PlanFeature>();\n\n for (const feature of features) {\n const quotaId = feature.quotaId || feature.quota?.id;\n if (!quotaId || !feature.quota) continue;\n\n const existing = quotaMap.get(quotaId);\n if (existing && existing.quota) {\n // Aggregate limits - sum numeric values\n const existingLimits = existing.quota.limits || {};\n const newLimits = feature.quota.limits || {};\n const combinedLimits: Record<string, number | string | boolean> = { ...existingLimits };\n\n for (const [key, value] of Object.entries(newLimits)) {\n if (typeof value === 'number' && typeof combinedLimits[key] === 'number') {\n // Sum numeric values (but keep -1/unlimited as unlimited)\n const existingVal = combinedLimits[key] as number;\n if (existingVal === -1 || value === -1) {\n combinedLimits[key] = -1; // Unlimited stays unlimited\n } else {\n combinedLimits[key] = existingVal + value;\n }\n } else if (!(key in combinedLimits)) {\n combinedLimits[key] = value;\n }\n }\n\n existing.quota = {\n ...existing.quota,\n limits: combinedLimits,\n };\n } else {\n // Clone the feature to avoid mutating original\n quotaMap.set(quotaId, {\n ...feature,\n quota: feature.quota\n ? {\n ...feature.quota,\n limits: feature.quota.limits ? { ...feature.quota.limits } : undefined,\n }\n : undefined,\n });\n }\n }\n\n return Array.from(quotaMap.values());\n};\n\n// Collapsible Features Component\ninterface CollapsibleFeaturesProps {\n features: PlanFeature[];\n isExpanded: boolean;\n onToggle: () => void;\n variant?: 'current' | 'available';\n}\n\nconst CollapsibleFeatures: FC<CollapsibleFeaturesProps> = ({\n features,\n isExpanded,\n onToggle,\n variant = 'available',\n}) => {\n // Aggregate features with same quotaId\n const aggregatedFeatures = useMemo(() => aggregateFeaturesByQuota(features), [features]);\n const featureCount = aggregatedFeatures.length;\n\n if (featureCount === 0) {\n return <p className=\"text-xs text-text-secondary italic\">No features defined</p>;\n }\n\n return (\n <div className=\"mt-2\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onToggle();\n }}\n className={`flex items-center gap-1.5 text-xs font-medium transition-colors ${\n variant === 'current'\n ? 'text-text-secondary hover:text-text-primary'\n : 'text-text-link hover:text-text-link'\n }`}\n >\n <Sparkles className=\"size-3\" />\n <span>\n {featureCount} feature{featureCount !== 1 ? 's' : ''}\n </span>\n {isExpanded ? <ChevronUp className=\"size-3\" /> : <ChevronDown className=\"size-3\" />}\n </button>\n\n {isExpanded && (\n <div className={`mt-2 space-y-2 ${variant === 'current' ? 'pl-0' : 'pl-1'}`}>\n {aggregatedFeatures.map((feature) => {\n const quota = feature.quota;\n if (!quota) return null;\n\n const limits = quota.limits || {};\n const limitEntries = Object.entries(limits);\n\n return (\n <div\n key={feature.id}\n className={`rounded-button p-2 ${\n variant === 'current' ? 'bg-bg-sunken' : 'bg-[var(--color-accent-soft)]'\n }`}\n >\n <p\n className={`text-xs font-medium ${\n variant === 'current' ? 'text-text-primary' : 'text-text-primary'\n }`}\n >\n {quota.name}\n </p>\n {limitEntries.length > 0 && (\n <div className=\"mt-1 flex flex-wrap gap-x-3 gap-y-1\">\n {limitEntries.map(([key, value]) => (\n <div\n key={key}\n className=\"flex items-center gap-1 text-xs text-text-secondary\"\n >\n <span className=\"opacity-70\">{formatLimitKey(key)}:</span>\n <span className=\"font-medium text-text-primary flex items-center gap-0.5\">\n {value === -1 || value === Infinity ? (\n <>\n <InfinityIcon className=\"size-3\" />\n <span className=\"sr-only\">Unlimited</span>\n </>\n ) : (\n formatLimitValue(value as number | string | boolean)\n )}\n </span>\n </div>\n ))}\n </div>\n )}\n {quota.reusable && (\n <span className=\"inline-block mt-1 text-[10px] px-1.5 py-0.5 rounded bg-status-success-bg-subtle text-status-success-text\">\n Reusable\n </span>\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n );\n};\n\nexport const UpgradeSubscriptionPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const { subscriptionId } = useParams<{ subscriptionId: string }>();\n const navigateTo = useBillingNavigate();\n const { orgId } = useBilling();\n const permissions = useBillingPermissions();\n const { selectedAccountId } = useBillingAccountSelection({\n orgId,\n syncFromUrl: true,\n urlParamName: 'billingAccountId',\n });\n\n const { data: defaultBillingAccountData } = useGetDefaultBillingAccountDashboardQuery({\n fetchPolicy: 'cache-first',\n });\n const billingAccountId =\n selectedAccountId ?? defaultBillingAccountData?.getDefaultBillingAccount?.id ?? undefined;\n const { subscription, isLoading: subscriptionLoading } = useSubscription(\n subscriptionId,\n billingAccountId\n );\n\n // Fetch available plans (includes features)\n const { data: plansData, loading: plansLoading } = useGetAllPlansQuery();\n\n // State\n const [selectedPlanId, setSelectedPlanId] = useState<string | null>(null);\n const [expandedFeatures, setExpandedFeatures] = useState<Record<string, boolean>>({});\n\n // Get all plans including current for feature lookup\n const allPlans = useMemo(() => {\n return (plansData?.getAllPlans || []) as Plan[];\n }, [plansData]);\n\n // Get current plan with features from allPlans\n const currentPlanWithFeatures = useMemo(() => {\n const currentPlanId = subscription?.planId;\n return allPlans.find((p) => p.id === currentPlanId) || null;\n }, [allPlans, subscription?.planId]);\n\n // Filter plans - show all other active plans (same product if productID exists, otherwise all)\n const availablePlans = useMemo(() => {\n const currentPlanId = subscription?.planId;\n const currentProductId = subscription?.plan?.productID;\n\n return allPlans\n .filter((plan) => {\n // Always exclude current plan\n if (plan.id === currentPlanId) return false;\n // Only show active plans\n if (!plan.isActive) return false;\n // If we have productID, filter by same product\n if (currentProductId) {\n return plan.productID === currentProductId;\n }\n // If no productID, show all active plans\n return true;\n })\n .sort((a, b) => a.price - b.price);\n }, [allPlans, subscription]);\n\n const selectedPlan = useMemo(() => {\n return availablePlans.find((p) => p.id === selectedPlanId) || null;\n }, [availablePlans, selectedPlanId]);\n\n const isDowngrade =\n selectedPlan !== null && selectedPlan.price < (subscription?.plan?.price ?? 0);\n\n // Toggle feature expansion\n const toggleFeatures = (planId: string) => {\n setExpandedFeatures((prev) => ({\n ...prev,\n [planId]: !prev[planId],\n }));\n };\n\n // Handle proceed to checkout\n const handleProceedToCheckout = () => {\n if (!subscription || !selectedPlan) return;\n const currentPrice = subscription.plan?.price ?? 0;\n const isDowngrade = selectedPlan.price < currentPrice;\n navigateTo(`/checkout/${selectedPlan.id}`, {\n state: {\n purchaseType: 'PLAN',\n // Upgrade: old quotas transfer to new plan immediately\n // Downgrade: old plan runs to natural expiry, auto-renewal cancelled\n ...(isDowngrade\n ? { downgradeFromSubscriptionId: subscription.id }\n : { upgradeFromSubscriptionId: subscription.id }),\n currentPlanName: subscription.plan?.name,\n billingAccountId: subscription.billingAccountId,\n },\n });\n };\n\n // Permission check\n if (!permissions.canUpdateSubscription) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <div className=\"size-12 mx-auto rounded-full bg-bg-sunken flex items-center justify-center\">\n <AlertCircle className=\"size-6 text-text-secondary\" />\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary max-w-sm\">\n You don't have permission to change subscriptions.\n </p>\n </div>\n </div>\n );\n }\n\n // Loading state\n if (subscriptionLoading || plansLoading) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <Loader2 className=\"size-8 animate-spin mx-auto text-text-link\" />\n <p className=\"text-text-secondary\">Loading plans…</p>\n </div>\n </div>\n );\n }\n\n // Subscription not found\n if (!subscription) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <AlertCircle className=\"size-12 mx-auto text-status-error-text\" />\n <h2 className=\"text-lg font-semibold text-text-primary\">Subscription not found</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo(withBillingAccountId('/subscriptions', billingAccountId))}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button\"\n >\n {tr('billing.subscriptions.backToSubscriptions', 'Back to Subscriptions')}\n </button>\n </div>\n </div>\n );\n }\n\n // Subscription is not active (already upgraded/canceled/etc)\n const isActive = subscription.status === ('active' as SubscriptionStatus);\n if (!isActive) {\n return (\n <div className=\"max-w-2xl mx-auto p-6 space-y-6\">\n <button\n type=\"button\"\n onClick={() =>\n navigateTo(withBillingAccountId(`/subscriptions/${subscriptionId}`, billingAccountId))\n }\n className=\"flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('billing.subscriptions.backToSubscription', 'Back to Subscription')}\n </button>\n\n <div className=\"text-center space-y-4 py-12\">\n <AlertCircle className=\"size-12 mx-auto text-status-warning-text\" />\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.subscriptions.cannotChangePlan', 'Cannot Change Plan')}\n </h2>\n <p className=\"text-sm text-text-secondary max-w-md mx-auto\">\n {tr(\n 'billing.subscriptions.notActiveMessage',\n 'This subscription is no longer active. You can only change plans for active subscriptions.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => navigateTo(withBillingAccountId('/subscriptions', billingAccountId))}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button\"\n >\n {tr('billing.subscriptions.viewAll', 'View All Subscriptions')}\n </button>\n </div>\n </div>\n );\n }\n\n // No other plans available\n if (availablePlans.length === 0) {\n return (\n <div className=\"max-w-2xl mx-auto p-6 space-y-6\">\n <button\n type=\"button\"\n onClick={() =>\n navigateTo(withBillingAccountId(`/subscriptions/${subscriptionId}`, billingAccountId))\n }\n className=\"flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('billing.subscriptions.backToSubscription', 'Back to Subscription')}\n </button>\n\n <div className=\"text-center space-y-4 py-12\">\n <Package className=\"size-12 mx-auto text-text-secondary\" />\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.subscriptions.noPlansAvailable', 'No Other Plans Available')}\n </h2>\n <p className=\"text-sm text-text-secondary max-w-md mx-auto\">\n {tr(\n 'billing.subscriptions.noPlansMessage',\n 'There are no other active plans available for this product.'\n )}\n </p>\n </div>\n </div>\n );\n }\n\n const currentPlanFeatures = (currentPlanWithFeatures?.features || []) as PlanFeature[];\n\n return (\n <div className=\"max-w-3xl mx-auto p-6 space-y-6\">\n {/* Header */}\n <div>\n <button\n type=\"button\"\n onClick={() =>\n navigateTo(withBillingAccountId(`/subscriptions/${subscriptionId}`, billingAccountId))\n }\n className=\"flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary mb-4\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('billing.subscriptions.backToSubscription', 'Back to Subscription')}\n </button>\n\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n {tr('billing.subscriptions.changeYourPlan', 'Change Your Plan')}\n </h1>\n <p className=\"text-text-secondary mt-1\">\n Switch from <span className=\"font-medium\">{subscription.plan?.name}</span> to a different\n plan\n </p>\n </div>\n\n {/* Current Plan Card */}\n <div className=\"bg-bg-sunken border border-border-subtle rounded-lg p-4\">\n <div className=\"flex items-start justify-between\">\n <div className=\"flex-1\">\n <p className=\"text-sm text-text-secondary\">Current Plan</p>\n <p className=\"font-semibold text-text-primary\">{subscription.plan?.name}</p>\n\n {/* Current Plan Features - Collapsible */}\n <CollapsibleFeatures\n features={currentPlanFeatures}\n isExpanded={expandedFeatures['current'] || false}\n onToggle={() => toggleFeatures('current')}\n variant=\"current\"\n />\n </div>\n <div className=\"text-right shrink-0 ml-4\">\n <p className=\"text-sm text-text-secondary\">Price</p>\n <p className=\"font-semibold text-text-primary\">\n {formatCurrency(subscription.plan?.price || 0, subscription.plan?.currency || 'USD')}\n <span className=\"text-sm font-normal text-text-secondary\">\n {' '}\n / {formatPlanDuration((subscription.plan?.duration || 'monthly') as PlanDuration)}\n </span>\n </p>\n </div>\n </div>\n </div>\n\n {/* Available Plans */}\n <div className=\"space-y-3\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Select New Plan</h2>\n <div className=\"grid gap-3\">\n {availablePlans.map((plan) => {\n const priceDiff = plan.price - (subscription.plan?.price || 0);\n const isSelected = selectedPlanId === plan.id;\n const planFeatures = (plan.features || []) as PlanFeature[];\n const isFeaturesExpanded = expandedFeatures[plan.id] || false;\n\n return (\n <div\n key={plan.id}\n className={`rounded-lg border transition-all ${\n isSelected\n ? 'border-border-strong bg-[var(--color-accent-soft)] ring-2 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle bg-bg-surface hover:border-border-strong'\n }`}\n >\n {/* Plan Header - Clickable for selection */}\n <button\n type=\"button\"\n onClick={() => setSelectedPlanId(plan.id)}\n className=\"w-full p-4 text-left\"\n >\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-start gap-3\">\n <div\n className={`size-5 mt-0.5 rounded-full border-2 flex items-center justify-center shrink-0 ${\n isSelected\n ? 'border-border-strong bg-action-primary-bg'\n : 'border-border-default'\n }`}\n >\n {isSelected && <CheckCircle className=\"size-3 text-action-primary-text\" />}\n </div>\n <div className=\"flex-1\">\n <p className=\"font-semibold text-text-primary\">{plan.name}</p>\n {priceDiff !== 0 && (\n <p\n className={`text-sm ${priceDiff > 0 ? 'text-status-warning-text' : 'text-status-success-text'}`}\n >\n {priceDiff > 0 ? '+' : ''}\n {formatCurrency(priceDiff, plan.currency)} /{' '}\n {formatPlanDuration(plan.duration)}\n </p>\n )}\n </div>\n </div>\n <div className=\"text-right shrink-0 ml-4\">\n <p className=\"font-bold text-text-primary\">\n {formatCurrency(plan.price, plan.currency)}\n </p>\n <p className=\"text-sm text-text-secondary\">\n / {formatPlanDuration(plan.duration)}\n </p>\n </div>\n </div>\n </button>\n\n {/* Features Section - Below the main content */}\n <div className=\"px-4 pb-4 pt-0 ml-8\">\n <CollapsibleFeatures\n features={planFeatures}\n isExpanded={isFeaturesExpanded}\n onToggle={() => toggleFeatures(plan.id)}\n variant=\"available\"\n />\n </div>\n </div>\n );\n })}\n </div>\n </div>\n\n {/* Summary & Action */}\n {selectedPlan &&\n (() => {\n const currentAggregatedCount = aggregateFeaturesByQuota(currentPlanFeatures).length;\n const selectedPlanFeatures = (selectedPlan.features || []) as PlanFeature[];\n const selectedAggregatedCount = aggregateFeaturesByQuota(selectedPlanFeatures).length;\n\n return (\n <div className=\"bg-bg-surface border border-border-subtle rounded-lg p-4 space-y-4\">\n {/* Plan change visualization */}\n <div className=\"flex items-center justify-center gap-4 py-2\">\n <div className=\"text-center\">\n <p className=\"text-xs text-text-secondary\">From</p>\n <p className=\"font-medium text-text-primary\">{subscription.plan?.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {currentAggregatedCount} feature{currentAggregatedCount !== 1 ? 's' : ''}\n </p>\n </div>\n <ArrowRight className=\"size-5 text-text-secondary\" />\n <div className=\"text-center\">\n <p className=\"text-xs text-text-secondary\">To</p>\n <p className=\"font-medium text-text-link\">{selectedPlan.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {selectedAggregatedCount} feature{selectedAggregatedCount !== 1 ? 's' : ''}\n </p>\n </div>\n </div>\n\n {/* Info message */}\n <p className=\"text-sm text-text-secondary text-center\">\n You'll be taken to checkout to select payment method and complete the change.\n </p>\n\n {/* Action Buttons */}\n <div className=\"flex items-center justify-end gap-3 pt-2 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={() =>\n navigateTo(\n withBillingAccountId(`/subscriptions/${subscriptionId}`, billingAccountId)\n )\n }\n className=\"px-4 py-2 text-sm font-medium text-text-secondary hover:text-text-primary\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleProceedToCheckout}\n className=\"px-6 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n {isDowngrade ? 'Downgrade Plan' : 'Upgrade Plan'}\n </button>\n </div>\n </div>\n );\n })()}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"UpgradeSubscriptionPage.js","names":[],"sources":["../../../../../src/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.tsx"],"sourcesContent":["/**\n * Change Plan Page\n * Allows users to change their current subscription to a different plan\n * Shows plan features in collapsible sections for easy comparison\n */\n\nimport { type FC, useState, useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useBillingAccountSelection } from '../../../hooks/useBillingAccountSelection';\nimport {\n ArrowLeft,\n ArrowRight,\n Loader2,\n Package,\n CheckCircle,\n AlertCircle,\n ChevronDown,\n ChevronUp,\n Sparkles,\n Infinity as InfinityIcon,\n} from 'lucide-react';\n\nimport { useSubscription } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { formatCurrency, formatPlanDuration } from '../../../shared/utils/format';\nimport {\n useGetAllPlansQuery,\n useGetDefaultBillingAccountDashboardQuery,\n} from '../../../../generated/global-operations';\nimport type { Plan, PlanDuration, SubscriptionStatus } from '../../../shared/types';\nimport { withBillingAccountId } from '../../../shared/utils/navigation';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\n\n// Helper type for plan features\ninterface PlanFeature {\n id: string;\n quotaId?: string;\n quota?: {\n id: string;\n name: string;\n limits?: Record<string, number | string | boolean>;\n reusable?: boolean;\n quotaType?: string;\n };\n}\n\n// Helper function to format limit values nicely\nconst formatLimitValue = (value: number | string | boolean): string => {\n if (typeof value === 'boolean') {\n return value ? 'Yes' : 'No';\n }\n if (typeof value === 'number') {\n if (value === -1 || value === Infinity) return 'Unlimited';\n if (value >= 1000000) return `${(value / 1000000).toFixed(1)}M`;\n if (value >= 1000) return `${(value / 1000).toFixed(1)}K`;\n return value.toLocaleString();\n }\n return String(value);\n};\n\n// Helper function to get a user-friendly limit key name\nconst formatLimitKey = (key: string): string => {\n return key\n .replace(/_/g, ' ')\n .replace(/([A-Z])/g, ' $1')\n .replace(/^./, (str) => str.toUpperCase())\n .trim();\n};\n\n// Helper function to aggregate features by quotaId\n// Combines limits for features with the same quota\nconst aggregateFeaturesByQuota = (features: PlanFeature[]): PlanFeature[] => {\n const quotaMap = new Map<string, PlanFeature>();\n\n for (const feature of features) {\n const quotaId = feature.quotaId || feature.quota?.id;\n if (!quotaId || !feature.quota) continue;\n\n const existing = quotaMap.get(quotaId);\n if (existing && existing.quota) {\n // Aggregate limits - sum numeric values\n const existingLimits = existing.quota.limits || {};\n const newLimits = feature.quota.limits || {};\n const combinedLimits: Record<string, number | string | boolean> = { ...existingLimits };\n\n for (const [key, value] of Object.entries(newLimits)) {\n if (typeof value === 'number' && typeof combinedLimits[key] === 'number') {\n // Sum numeric values (but keep -1/unlimited as unlimited)\n const existingVal = combinedLimits[key] as number;\n if (existingVal === -1 || value === -1) {\n combinedLimits[key] = -1; // Unlimited stays unlimited\n } else {\n combinedLimits[key] = existingVal + value;\n }\n } else if (!(key in combinedLimits)) {\n combinedLimits[key] = value;\n }\n }\n\n existing.quota = {\n ...existing.quota,\n limits: combinedLimits,\n };\n } else {\n // Clone the feature to avoid mutating original\n quotaMap.set(quotaId, {\n ...feature,\n quota: feature.quota\n ? {\n ...feature.quota,\n limits: feature.quota.limits ? { ...feature.quota.limits } : undefined,\n }\n : undefined,\n });\n }\n }\n\n return Array.from(quotaMap.values());\n};\n\n// Collapsible Features Component\ninterface CollapsibleFeaturesProps {\n features: PlanFeature[];\n isExpanded: boolean;\n onToggle: () => void;\n variant?: 'current' | 'available';\n}\n\nconst CollapsibleFeatures: FC<CollapsibleFeaturesProps> = ({\n features,\n isExpanded,\n onToggle,\n variant = 'available',\n}) => {\n // Aggregate features with same quotaId\n const aggregatedFeatures = useMemo(() => aggregateFeaturesByQuota(features), [features]);\n const featureCount = aggregatedFeatures.length;\n\n if (featureCount === 0) {\n return <p className=\"text-xs text-text-secondary italic\">No features defined</p>;\n }\n\n return (\n <div className=\"mt-2\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onToggle();\n }}\n className={`flex items-center gap-1.5 text-xs font-medium transition-colors ${\n variant === 'current'\n ? 'text-text-secondary hover:text-text-primary'\n : 'text-text-link hover:text-text-link'\n }`}\n >\n <Sparkles className=\"size-3\" />\n <span>\n {featureCount} feature{featureCount !== 1 ? 's' : ''}\n </span>\n {isExpanded ? <ChevronUp className=\"size-3\" /> : <ChevronDown className=\"size-3\" />}\n </button>\n\n {isExpanded && (\n <div className={`mt-2 space-y-2 ${variant === 'current' ? 'pl-0' : 'pl-1'}`}>\n {aggregatedFeatures.map((feature) => {\n const quota = feature.quota;\n if (!quota) return null;\n\n const limits = quota.limits || {};\n const limitEntries = Object.entries(limits);\n\n return (\n <div\n key={feature.id}\n className={`rounded-button p-2 ${\n variant === 'current' ? 'bg-bg-sunken' : 'bg-[var(--color-accent-soft)]'\n }`}\n >\n <p\n className={`text-xs font-medium ${\n variant === 'current' ? 'text-text-primary' : 'text-text-primary'\n }`}\n >\n {quota.name}\n </p>\n {limitEntries.length > 0 && (\n <div className=\"mt-1 flex flex-wrap gap-x-3 gap-y-1\">\n {limitEntries.map(([key, value]) => (\n <div\n key={key}\n className=\"flex items-center gap-1 text-xs text-text-secondary\"\n >\n <span className=\"opacity-70\">{formatLimitKey(key)}:</span>\n <span className=\"font-medium text-text-primary flex items-center gap-0.5\">\n {value === -1 || value === Infinity ? (\n <>\n <InfinityIcon className=\"size-3\" />\n <span className=\"sr-only\">Unlimited</span>\n </>\n ) : (\n formatLimitValue(value as number | string | boolean)\n )}\n </span>\n </div>\n ))}\n </div>\n )}\n {quota.reusable && (\n <span className=\"inline-block mt-1 text-[10px] px-1.5 py-0.5 rounded bg-status-success-bg-subtle text-status-success-text\">\n Reusable\n </span>\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n );\n};\n\nexport const UpgradeSubscriptionPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const { subscriptionId } = useParams<{ subscriptionId: string }>();\n const navigateTo = useBillingNavigate();\n const { orgId } = useBilling();\n const permissions = useBillingPermissions();\n const { selectedAccountId } = useBillingAccountSelection({\n orgId,\n syncFromUrl: true,\n urlParamName: 'billingAccountId',\n });\n\n const { data: defaultBillingAccountData } = useGetDefaultBillingAccountDashboardQuery({\n fetchPolicy: 'cache-first',\n });\n const billingAccountId =\n selectedAccountId ?? defaultBillingAccountData?.getDefaultBillingAccount?.id ?? undefined;\n const { subscription, isLoading: subscriptionLoading } = useSubscription(\n subscriptionId,\n billingAccountId\n );\n\n // Fetch available plans (includes features)\n const { data: plansData, loading: plansLoading } = useGetAllPlansQuery();\n\n // State\n const [selectedPlanId, setSelectedPlanId] = useState<string | null>(null);\n const [expandedFeatures, setExpandedFeatures] = useState<Record<string, boolean>>({});\n\n // Get all plans including current for feature lookup\n const allPlans = useMemo(() => {\n return (plansData?.getAllPlans || []) as Plan[];\n }, [plansData]);\n\n // Get current plan with features from allPlans\n const currentPlanWithFeatures = useMemo(() => {\n const currentPlanId = subscription?.planId;\n return allPlans.find((p) => p.id === currentPlanId) || null;\n }, [allPlans, subscription?.planId]);\n\n // Filter plans - show all other active plans (same product if productID exists, otherwise all)\n const availablePlans = useMemo(() => {\n const currentPlanId = subscription?.planId;\n const currentProductId = subscription?.plan?.productID;\n\n return allPlans\n .filter((plan) => {\n // Always exclude current plan\n if (plan.id === currentPlanId) return false;\n // Only show active plans\n if (!plan.isActive) return false;\n // If we have productID, filter by same product\n if (currentProductId) {\n return plan.productID === currentProductId;\n }\n // If no productID, show all active plans\n return true;\n })\n .sort((a, b) => a.price - b.price);\n }, [allPlans, subscription]);\n\n const selectedPlan = useMemo(() => {\n return availablePlans.find((p) => p.id === selectedPlanId) || null;\n }, [availablePlans, selectedPlanId]);\n\n const isDowngrade =\n selectedPlan !== null && selectedPlan.price < (subscription?.plan?.price ?? 0);\n\n // Toggle feature expansion\n const toggleFeatures = (planId: string) => {\n setExpandedFeatures((prev) => ({\n ...prev,\n [planId]: !prev[planId],\n }));\n };\n\n // Handle proceed to checkout\n const handleProceedToCheckout = () => {\n if (!subscription || !selectedPlan) return;\n const currentPrice = subscription.plan?.price ?? 0;\n const isDowngrade = selectedPlan.price < currentPrice;\n navigateTo(`/checkout/${selectedPlan.id}`, {\n state: {\n purchaseType: 'PLAN',\n // Upgrade: old quotas transfer to new plan immediately\n // Downgrade: old plan runs to natural expiry, auto-renewal cancelled\n ...(isDowngrade\n ? { downgradeFromSubscriptionId: subscription.id }\n : { upgradeFromSubscriptionId: subscription.id }),\n currentPlanName: subscription.plan?.name,\n billingAccountId: subscription.billingAccountId,\n },\n });\n };\n\n // Permission check\n if (!permissions.canUpdateSubscription) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <div className=\"size-12 mx-auto rounded-full bg-bg-sunken flex items-center justify-center\">\n <AlertCircle className=\"size-6 text-text-secondary\" />\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary max-w-sm\">\n You don't have permission to change subscriptions.\n </p>\n </div>\n </div>\n );\n }\n\n // Loading state\n if (subscriptionLoading || plansLoading) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <Loader2 className=\"size-8 animate-spin mx-auto text-text-link\" />\n <p className=\"text-text-secondary\">Loading plans…</p>\n </div>\n </div>\n );\n }\n\n // Subscription not found\n if (!subscription) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <AlertCircle className=\"size-12 mx-auto text-status-error-text\" />\n <h2 className=\"text-lg font-semibold text-text-primary\">Subscription not found</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo(withBillingAccountId('/subscriptions', billingAccountId))}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button\"\n >\n {tr('billing.subscriptions.backToSubscriptions', 'Back to Subscriptions')}\n </button>\n </div>\n </div>\n );\n }\n\n // Subscription is not active (already upgraded/canceled/etc)\n const isActive = subscription.status === ('active' as SubscriptionStatus);\n if (!isActive) {\n return (\n <div className=\"max-w-2xl mx-auto p-6 space-y-6\">\n <button\n type=\"button\"\n onClick={() =>\n navigateTo(withBillingAccountId(`/subscriptions/${subscriptionId}`, billingAccountId))\n }\n className=\"flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('billing.subscriptions.backToSubscription', 'Back to Subscription')}\n </button>\n\n <div className=\"text-center space-y-4 py-12\">\n <AlertCircle className=\"size-12 mx-auto text-status-warning-text\" />\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.subscriptions.cannotChangePlan', 'Cannot Change Plan')}\n </h2>\n <p className=\"text-sm text-text-secondary max-w-md mx-auto\">\n {tr(\n 'billing.subscriptions.notActiveMessage',\n 'This subscription is no longer active. You can only change plans for active subscriptions.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => navigateTo(withBillingAccountId('/subscriptions', billingAccountId))}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button\"\n >\n {tr('billing.subscriptions.viewAll', 'View All Subscriptions')}\n </button>\n </div>\n </div>\n );\n }\n\n // No other plans available\n if (availablePlans.length === 0) {\n return (\n <div className=\"max-w-2xl mx-auto p-6 space-y-6\">\n <button\n type=\"button\"\n onClick={() =>\n navigateTo(withBillingAccountId(`/subscriptions/${subscriptionId}`, billingAccountId))\n }\n className=\"flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('billing.subscriptions.backToSubscription', 'Back to Subscription')}\n </button>\n\n <div className=\"text-center space-y-4 py-12\">\n <Package className=\"size-12 mx-auto text-text-secondary\" />\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.subscriptions.noPlansAvailable', 'No Other Plans Available')}\n </h2>\n <p className=\"text-sm text-text-secondary max-w-md mx-auto\">\n {tr(\n 'billing.subscriptions.noPlansMessage',\n 'There are no other active plans available for this product.'\n )}\n </p>\n </div>\n </div>\n );\n }\n\n const currentPlanFeatures = (currentPlanWithFeatures?.features || []) as PlanFeature[];\n\n return (\n <div className=\"max-w-3xl mx-auto p-6 space-y-6\">\n {/* Header */}\n <div>\n <button\n type=\"button\"\n onClick={() =>\n navigateTo(withBillingAccountId(`/subscriptions/${subscriptionId}`, billingAccountId))\n }\n className=\"flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary mb-4\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('billing.subscriptions.backToSubscription', 'Back to Subscription')}\n </button>\n\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n {tr('billing.subscriptions.changeYourPlan', 'Change Your Plan')}\n </h1>\n <p className=\"text-text-secondary mt-1\">\n Switch from <span className=\"font-medium\">{subscription.plan?.name}</span> to a different\n plan\n </p>\n <PagePurpose className=\"mt-3\">\n {tr(\n 'billing.subscriptions.upgradePurpose',\n 'Move this subscription to a different plan — upgrade for more capacity or downgrade to save. Compare what each plan includes and see how the change affects your billing before you confirm.'\n )}\n </PagePurpose>\n </div>\n\n {/* Current Plan Card */}\n <div className=\"bg-bg-sunken border border-border-subtle rounded-lg p-4\">\n <div className=\"flex items-start justify-between\">\n <div className=\"flex-1\">\n <p className=\"text-sm text-text-secondary\">Current Plan</p>\n <p className=\"font-semibold text-text-primary\">{subscription.plan?.name}</p>\n\n {/* Current Plan Features - Collapsible */}\n <CollapsibleFeatures\n features={currentPlanFeatures}\n isExpanded={expandedFeatures['current'] || false}\n onToggle={() => toggleFeatures('current')}\n variant=\"current\"\n />\n </div>\n <div className=\"text-right shrink-0 ml-4\">\n <p className=\"text-sm text-text-secondary\">Price</p>\n <p className=\"font-semibold text-text-primary\">\n {formatCurrency(subscription.plan?.price || 0, subscription.plan?.currency || 'USD')}\n <span className=\"text-sm font-normal text-text-secondary\">\n {' '}\n / {formatPlanDuration((subscription.plan?.duration || 'monthly') as PlanDuration)}\n </span>\n </p>\n </div>\n </div>\n </div>\n\n {/* Available Plans */}\n <div className=\"space-y-3\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Select New Plan</h2>\n <div className=\"grid gap-3\">\n {availablePlans.map((plan) => {\n const priceDiff = plan.price - (subscription.plan?.price || 0);\n const isSelected = selectedPlanId === plan.id;\n const planFeatures = (plan.features || []) as PlanFeature[];\n const isFeaturesExpanded = expandedFeatures[plan.id] || false;\n\n return (\n <div\n key={plan.id}\n className={`rounded-lg border transition-all ${\n isSelected\n ? 'border-border-strong bg-[var(--color-accent-soft)] ring-2 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle bg-bg-surface hover:border-border-strong'\n }`}\n >\n {/* Plan Header - Clickable for selection */}\n <button\n type=\"button\"\n onClick={() => setSelectedPlanId(plan.id)}\n className=\"w-full p-4 text-left\"\n >\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-start gap-3\">\n <div\n className={`size-5 mt-0.5 rounded-full border-2 flex items-center justify-center shrink-0 ${\n isSelected\n ? 'border-border-strong bg-action-primary-bg'\n : 'border-border-default'\n }`}\n >\n {isSelected && <CheckCircle className=\"size-3 text-action-primary-text\" />}\n </div>\n <div className=\"flex-1\">\n <p className=\"font-semibold text-text-primary\">{plan.name}</p>\n {priceDiff !== 0 && (\n <p\n className={`text-sm ${priceDiff > 0 ? 'text-status-warning-text' : 'text-status-success-text'}`}\n >\n {priceDiff > 0 ? '+' : ''}\n {formatCurrency(priceDiff, plan.currency)} /{' '}\n {formatPlanDuration(plan.duration)}\n </p>\n )}\n </div>\n </div>\n <div className=\"text-right shrink-0 ml-4\">\n <p className=\"font-bold text-text-primary\">\n {formatCurrency(plan.price, plan.currency)}\n </p>\n <p className=\"text-sm text-text-secondary\">\n / {formatPlanDuration(plan.duration)}\n </p>\n </div>\n </div>\n </button>\n\n {/* Features Section - Below the main content */}\n <div className=\"px-4 pb-4 pt-0 ml-8\">\n <CollapsibleFeatures\n features={planFeatures}\n isExpanded={isFeaturesExpanded}\n onToggle={() => toggleFeatures(plan.id)}\n variant=\"available\"\n />\n </div>\n </div>\n );\n })}\n </div>\n </div>\n\n {/* Summary & Action */}\n {selectedPlan &&\n (() => {\n const currentAggregatedCount = aggregateFeaturesByQuota(currentPlanFeatures).length;\n const selectedPlanFeatures = (selectedPlan.features || []) as PlanFeature[];\n const selectedAggregatedCount = aggregateFeaturesByQuota(selectedPlanFeatures).length;\n\n return (\n <div className=\"bg-bg-surface border border-border-subtle rounded-lg p-4 space-y-4\">\n {/* Plan change visualization */}\n <div className=\"flex items-center justify-center gap-4 py-2\">\n <div className=\"text-center\">\n <p className=\"text-xs text-text-secondary\">From</p>\n <p className=\"font-medium text-text-primary\">{subscription.plan?.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {currentAggregatedCount} feature{currentAggregatedCount !== 1 ? 's' : ''}\n </p>\n </div>\n <ArrowRight className=\"size-5 text-text-secondary\" />\n <div className=\"text-center\">\n <p className=\"text-xs text-text-secondary\">To</p>\n <p className=\"font-medium text-text-link\">{selectedPlan.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {selectedAggregatedCount} feature{selectedAggregatedCount !== 1 ? 's' : ''}\n </p>\n </div>\n </div>\n\n {/* Info message */}\n <p className=\"text-sm text-text-secondary text-center\">\n You'll be taken to checkout to select payment method and complete the change.\n </p>\n\n {/* Action Buttons */}\n <div className=\"flex items-center justify-end gap-3 pt-2 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={() =>\n navigateTo(\n withBillingAccountId(`/subscriptions/${subscriptionId}`, billingAccountId)\n )\n }\n className=\"px-4 py-2 text-sm font-medium text-text-secondary hover:text-text-primary\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleProceedToCheckout}\n className=\"px-6 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n {isDowngrade ? 'Downgrade Plan' : 'Upgrade Plan'}\n </button>\n </div>\n </div>\n );\n })()}\n </div>\n );\n};\n"],"mappings":"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"}
|