@lukoweb/apitogo-plugin-dev-portal-billing 0.1.50 → 0.1.53
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/index.d.mts +12 -3
- package/dist/index.mjs +177 -64
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as _lukoweb_apitogo0 from "@lukoweb/apitogo";
|
|
2
1
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
2
|
+
import * as _lukoweb_apitogo_plugins0 from "@lukoweb/apitogo/plugins";
|
|
3
3
|
|
|
4
4
|
//#region src/pages/AccountPlansPage.d.ts
|
|
5
5
|
declare const AccountPlansPage: () => react_jsx_runtime0.JSX.Element;
|
|
@@ -9,7 +9,16 @@ type DevPortalBillingConfig = {
|
|
|
9
9
|
pricing?: {
|
|
10
10
|
title?: string;
|
|
11
11
|
subtitle?: string;
|
|
12
|
-
};
|
|
12
|
+
}; /** Local preview plan catalog (from `apitogo.config.json` `plans`). */
|
|
13
|
+
previewPlans?: Array<{
|
|
14
|
+
sku: string;
|
|
15
|
+
displayName: string;
|
|
16
|
+
priceAmount?: number;
|
|
17
|
+
priceCurrency?: string;
|
|
18
|
+
billingPeriod?: string;
|
|
19
|
+
limitsJson?: string | null;
|
|
20
|
+
includedActionKeys?: string[];
|
|
21
|
+
}>;
|
|
13
22
|
};
|
|
14
23
|
//#endregion
|
|
15
24
|
//#region src/paths.d.ts
|
|
@@ -19,6 +28,6 @@ declare const SUBSCRIPTIONS_PATH = "/account/subscriptions";
|
|
|
19
28
|
declare const CREDENTIALS_PATH = "/account/credentials";
|
|
20
29
|
//#endregion
|
|
21
30
|
//#region src/DevPortalBillingPlugin.d.ts
|
|
22
|
-
declare const devPortalBillingPlugin: (options?: DevPortalBillingConfig | undefined) =>
|
|
31
|
+
declare const devPortalBillingPlugin: (options?: DevPortalBillingConfig | undefined) => _lukoweb_apitogo_plugins0.ApitogoPlugin;
|
|
23
32
|
//#endregion
|
|
24
33
|
export { AccountPlansPage, CHECKOUT_PATH, CREDENTIALS_PATH, PRICING_PATH, SUBSCRIPTIONS_PATH, devPortalBillingPlugin };
|
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { Button,
|
|
1
|
+
import { Button, Head, Heading } from "@lukoweb/apitogo/components";
|
|
2
2
|
import { CreditCardIcon, KeyRoundIcon, ShieldIcon, StarsIcon } from "@lukoweb/apitogo/icons";
|
|
3
3
|
import { createContext, useContext, useEffect, useState } from "react";
|
|
4
|
-
import { cn, createPlugin } from "@lukoweb/apitogo";
|
|
5
4
|
import { Link, Navigate, Outlet, useNavigate, useSearchParams } from "@lukoweb/apitogo/router";
|
|
6
5
|
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@lukoweb/apitogo/ui/AlertDialog.js";
|
|
6
|
+
import { cn } from "@lukoweb/apitogo/ui/util.js";
|
|
7
7
|
import manifestCatalog from "virtual:apitogo-local-manifest";
|
|
8
|
+
import zudokuConfig from "virtual:zudoku-config";
|
|
8
9
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
10
|
import { useMutation, useQuery, useQueryClient } from "@lukoweb/apitogo/react-query";
|
|
10
11
|
import { useAuth } from "@lukoweb/apitogo/hooks";
|
|
12
|
+
import { createPlugin } from "@lukoweb/apitogo/plugins";
|
|
11
13
|
import { Alert, AlertAction, AlertDescription } from "@lukoweb/apitogo/ui/Alert";
|
|
12
14
|
import { Button as Button$1 } from "@lukoweb/apitogo/ui/Button";
|
|
13
15
|
import { Secret } from "@lukoweb/apitogo/ui/Secret.js";
|
|
@@ -18,25 +20,52 @@ const DEV_PORTAL_PLACEHOLDER_API_URL = "https://dp-example.azurecontainerapps.io
|
|
|
18
20
|
function resolveDevPortalApiBaseUrl(configuredUrl) {
|
|
19
21
|
const fromConfig = configuredUrl?.trim();
|
|
20
22
|
if (fromConfig) return fromConfig.replace(/\/+$/, "");
|
|
21
|
-
const envUrl = typeof VITE_APITOGO_DEV_PORTAL_API_URL === "string" ? VITE_APITOGO_DEV_PORTAL_API_URL.trim() : "";
|
|
23
|
+
const envUrl = (typeof VITE_APITOGO_AUTHENTICATION_API_BASE_URL === "string" ? VITE_APITOGO_AUTHENTICATION_API_BASE_URL.trim() : "") || (typeof VITE_APITOGO_DEV_PORTAL_API_URL === "string" ? VITE_APITOGO_DEV_PORTAL_API_URL.trim() : "");
|
|
22
24
|
return envUrl ? envUrl.replace(/\/+$/, "") : void 0;
|
|
23
25
|
}
|
|
24
26
|
function isPlaceholderDevPortalApiUrl(apiBaseUrl) {
|
|
25
27
|
if (!apiBaseUrl) return true;
|
|
26
28
|
return apiBaseUrl === DEV_PORTAL_PLACEHOLDER_API_URL || apiBaseUrl.includes("dp-example.azurecontainerapps.io");
|
|
27
29
|
}
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
return { plans: manifestCatalog.plans.map((plan) => ({
|
|
30
|
+
function mapPreviewPlans(plans) {
|
|
31
|
+
return plans.map((plan) => ({
|
|
31
32
|
sku: plan.sku,
|
|
32
33
|
displayName: plan.displayName,
|
|
33
|
-
isFree: plan.isFree,
|
|
34
34
|
priceAmount: plan.priceAmount,
|
|
35
35
|
priceCurrency: plan.priceCurrency,
|
|
36
36
|
billingPeriod: plan.billingPeriod,
|
|
37
37
|
limitsJson: plan.limitsJson,
|
|
38
38
|
includedActionKeys: plan.includedActionKeys
|
|
39
|
-
}))
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
function readModulePlanItems() {
|
|
42
|
+
const items = (zudokuConfig?.modules)?.userPanel?.plans?.items;
|
|
43
|
+
return Array.isArray(items) && items.length > 0 ? items : void 0;
|
|
44
|
+
}
|
|
45
|
+
function parseManifestPlans(configPlans) {
|
|
46
|
+
if (configPlans?.length) return { plans: mapPreviewPlans(configPlans.map((plan) => ({
|
|
47
|
+
sku: plan.sku,
|
|
48
|
+
displayName: plan.displayName,
|
|
49
|
+
priceAmount: plan.priceAmount ?? 0,
|
|
50
|
+
priceCurrency: plan.priceCurrency ?? "usd",
|
|
51
|
+
billingPeriod: plan.billingPeriod ?? "monthly",
|
|
52
|
+
limitsJson: plan.limitsJson ?? null,
|
|
53
|
+
includedActionKeys: plan.includedActionKeys ?? []
|
|
54
|
+
}))) };
|
|
55
|
+
if (manifestCatalog?.plans?.length) return { plans: mapPreviewPlans(manifestCatalog.plans) };
|
|
56
|
+
const metaPlans = zudokuConfig?.__meta?.previewPlans;
|
|
57
|
+
if (metaPlans?.length) return { plans: mapPreviewPlans(metaPlans) };
|
|
58
|
+
const moduleItems = readModulePlanItems();
|
|
59
|
+
if (moduleItems?.length) return { plans: mapPreviewPlans(moduleItems.map((plan) => ({
|
|
60
|
+
sku: plan.sku,
|
|
61
|
+
displayName: plan.displayName,
|
|
62
|
+
priceAmount: plan.priceAmount ?? 0,
|
|
63
|
+
priceCurrency: plan.priceCurrency ?? "usd",
|
|
64
|
+
billingPeriod: plan.billingPeriod ?? "monthly",
|
|
65
|
+
limitsJson: plan.limitsJson ?? null,
|
|
66
|
+
includedActionKeys: plan.includedActionKeys ?? []
|
|
67
|
+
}))) };
|
|
68
|
+
return { plans: [] };
|
|
40
69
|
}
|
|
41
70
|
function formatActionKey(actionKey) {
|
|
42
71
|
const separatorIndex = actionKey.indexOf(":");
|
|
@@ -44,7 +73,7 @@ function formatActionKey(actionKey) {
|
|
|
44
73
|
return `${actionKey.slice(0, separatorIndex).toUpperCase()} ${actionKey.slice(separatorIndex + 1)}`;
|
|
45
74
|
}
|
|
46
75
|
function formatPlanPrice(plan) {
|
|
47
|
-
if (plan.
|
|
76
|
+
if (plan.priceAmount <= 0) return "Free";
|
|
48
77
|
const amount = new Intl.NumberFormat(void 0, {
|
|
49
78
|
style: "currency",
|
|
50
79
|
currency: plan.priceCurrency.toUpperCase(),
|
|
@@ -55,6 +84,9 @@ function formatPlanPrice(plan) {
|
|
|
55
84
|
if (period === "annual" || period === "year") return `${amount}/yr`;
|
|
56
85
|
return amount;
|
|
57
86
|
}
|
|
87
|
+
function isPlanFree(plan) {
|
|
88
|
+
return plan.priceAmount <= 0;
|
|
89
|
+
}
|
|
58
90
|
function formatBillingPeriod(period) {
|
|
59
91
|
const normalized = period.toLowerCase();
|
|
60
92
|
if (normalized === "month" || normalized === "monthly") return "Monthly";
|
|
@@ -130,7 +162,7 @@ const PricingCard = ({ plan, mode = "public", subscriptionStatus, isSubscribed =
|
|
|
130
162
|
children: formatActionKey(actionKey)
|
|
131
163
|
}) }, actionKey))
|
|
132
164
|
})]
|
|
133
|
-
}) : plan
|
|
165
|
+
}) : isPlanFree(plan) ? /* @__PURE__ */ jsx("p", {
|
|
134
166
|
className: "mt-4 text-sm text-muted-foreground",
|
|
135
167
|
children: "API access not included"
|
|
136
168
|
}) : null,
|
|
@@ -171,7 +203,7 @@ const PricingCard = ({ plan, mode = "public", subscriptionStatus, isSubscribed =
|
|
|
171
203
|
asChild: true,
|
|
172
204
|
children: /* @__PURE__ */ jsx(Link, {
|
|
173
205
|
to: `/checkout?planSku=${encodeURIComponent(plan.sku)}`,
|
|
174
|
-
children: plan
|
|
206
|
+
children: isPlanFree(plan) ? "Get started" : "Subscribe"
|
|
175
207
|
})
|
|
176
208
|
}) : isActive ? /* @__PURE__ */ jsx(Button, {
|
|
177
209
|
variant: "secondary",
|
|
@@ -187,7 +219,7 @@ const PricingCard = ({ plan, mode = "public", subscriptionStatus, isSubscribed =
|
|
|
187
219
|
asChild: true,
|
|
188
220
|
children: /* @__PURE__ */ jsx(Link, {
|
|
189
221
|
to: `/checkout?planSku=${encodeURIComponent(plan.sku)}`,
|
|
190
|
-
children: plan
|
|
222
|
+
children: isPlanFree(plan) ? "Get started" : "Subscribe"
|
|
191
223
|
})
|
|
192
224
|
})
|
|
193
225
|
})
|
|
@@ -195,6 +227,15 @@ const PricingCard = ({ plan, mode = "public", subscriptionStatus, isSubscribed =
|
|
|
195
227
|
});
|
|
196
228
|
};
|
|
197
229
|
|
|
230
|
+
//#endregion
|
|
231
|
+
//#region src/DevPortalBillingContext.tsx
|
|
232
|
+
const DevPortalBillingContext = createContext({});
|
|
233
|
+
const DevPortalBillingProvider = ({ options, children }) => /* @__PURE__ */ jsx(DevPortalBillingContext.Provider, {
|
|
234
|
+
value: options,
|
|
235
|
+
children
|
|
236
|
+
});
|
|
237
|
+
const useDevPortalBillingConfig = () => useContext(DevPortalBillingContext);
|
|
238
|
+
|
|
198
239
|
//#endregion
|
|
199
240
|
//#region src/hooks/useDevPortalApiBaseUrl.ts
|
|
200
241
|
function useDevPortalApiBaseUrl() {
|
|
@@ -264,27 +305,45 @@ function useMySubscriptions() {
|
|
|
264
305
|
function usePlans() {
|
|
265
306
|
const apiBaseUrl = useDevPortalApiBaseUrl();
|
|
266
307
|
const isPreview = useDevPortalPreviewMode();
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
isPreview
|
|
272
|
-
],
|
|
308
|
+
const { previewPlans } = useDevPortalBillingConfig();
|
|
309
|
+
const liveQuery = useQuery({
|
|
310
|
+
queryKey: ["dev-portal-plans", apiBaseUrl],
|
|
311
|
+
enabled: !isPreview && Boolean(apiBaseUrl),
|
|
273
312
|
queryFn: async () => {
|
|
274
|
-
if (isPreview) return parseManifestPlans();
|
|
275
313
|
if (!apiBaseUrl) return { plans: [] };
|
|
276
314
|
const response = await fetch(`${apiBaseUrl}/api/v1/plans`, { headers: { Accept: "application/json" } });
|
|
277
315
|
if (!response.ok) throw new Error("Failed to load plans.");
|
|
278
316
|
return await response.json();
|
|
279
317
|
}
|
|
280
318
|
});
|
|
319
|
+
if (isPreview) {
|
|
320
|
+
const data = parseManifestPlans(previewPlans);
|
|
321
|
+
return {
|
|
322
|
+
...liveQuery,
|
|
323
|
+
data,
|
|
324
|
+
dataUpdatedAt: Date.now(),
|
|
325
|
+
error: null,
|
|
326
|
+
fetchStatus: "idle",
|
|
327
|
+
isError: false,
|
|
328
|
+
isFetching: false,
|
|
329
|
+
isLoading: false,
|
|
330
|
+
isPending: false,
|
|
331
|
+
isSuccess: true,
|
|
332
|
+
status: "success"
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
return liveQuery;
|
|
281
336
|
}
|
|
282
337
|
|
|
283
338
|
//#endregion
|
|
284
339
|
//#region src/pages/AccountPlansPage.tsx
|
|
285
340
|
const AccountPlansPage = () => {
|
|
341
|
+
const { previewPlans } = useDevPortalBillingConfig();
|
|
286
342
|
const isPreview = useDevPortalPreviewMode();
|
|
287
|
-
const
|
|
343
|
+
const livePlans = usePlans();
|
|
344
|
+
const catalog = isPreview ? parseManifestPlans(previewPlans) : livePlans.data;
|
|
345
|
+
const isLoading = isPreview ? false : livePlans.isLoading;
|
|
346
|
+
const isError = isPreview ? false : livePlans.isError;
|
|
288
347
|
const { data: me } = useMySubscriptions();
|
|
289
348
|
const cancelMutation = useCancelSubscription();
|
|
290
349
|
const [cancelingSku, setCancelingSku] = useState(null);
|
|
@@ -324,17 +383,16 @@ const AccountPlansPage = () => {
|
|
|
324
383
|
" ",
|
|
325
384
|
/* @__PURE__ */ jsx("code", {
|
|
326
385
|
className: "rounded bg-muted px-1.5 py-0.5",
|
|
327
|
-
children: "apitogo.json"
|
|
386
|
+
children: "apitogo.config.json"
|
|
328
387
|
}),
|
|
329
388
|
` `,
|
|
330
|
-
"(
|
|
389
|
+
"(overridable via",
|
|
331
390
|
" ",
|
|
332
391
|
/* @__PURE__ */ jsx("code", {
|
|
333
392
|
className: "rounded bg-muted px-1.5 py-0.5",
|
|
334
|
-
children: "
|
|
393
|
+
children: ".env"
|
|
335
394
|
}),
|
|
336
|
-
" "
|
|
337
|
-
"overrides). Subscribe and unsubscribe activate after the portal is published."
|
|
395
|
+
"). Subscribe and unsubscribe activate after the portal is published."
|
|
338
396
|
]
|
|
339
397
|
}) : null,
|
|
340
398
|
cancelMutation.isError ? /* @__PURE__ */ jsx("div", {
|
|
@@ -372,20 +430,11 @@ const AccountPlansPage = () => {
|
|
|
372
430
|
});
|
|
373
431
|
};
|
|
374
432
|
|
|
375
|
-
//#endregion
|
|
376
|
-
//#region src/DevPortalBillingContext.tsx
|
|
377
|
-
const DevPortalBillingContext = createContext({});
|
|
378
|
-
const DevPortalBillingProvider = ({ options, children }) => /* @__PURE__ */ jsx(DevPortalBillingContext.Provider, {
|
|
379
|
-
value: options,
|
|
380
|
-
children
|
|
381
|
-
});
|
|
382
|
-
const useDevPortalBillingConfig = () => useContext(DevPortalBillingContext);
|
|
383
|
-
|
|
384
433
|
//#endregion
|
|
385
434
|
//#region src/DevPortalBillingWrapper.tsx
|
|
386
435
|
const DevPortalBillingWrapper = ({ options = {} }) => /* @__PURE__ */ jsx(DevPortalBillingProvider, {
|
|
387
436
|
options,
|
|
388
|
-
children: /* @__PURE__ */ jsx(
|
|
437
|
+
children: /* @__PURE__ */ jsx(Outlet, {})
|
|
389
438
|
});
|
|
390
439
|
|
|
391
440
|
//#endregion
|
|
@@ -436,6 +485,7 @@ const PRICING_PATH = "/pricing";
|
|
|
436
485
|
const CHECKOUT_PATH = "/checkout";
|
|
437
486
|
const SUBSCRIPTIONS_PATH = "/account/subscriptions";
|
|
438
487
|
const CREDENTIALS_PATH = "/account/credentials";
|
|
488
|
+
const LOGIN_NAV_PATH = "/signin";
|
|
439
489
|
|
|
440
490
|
//#endregion
|
|
441
491
|
//#region src/pages/CheckoutPage.tsx
|
|
@@ -474,7 +524,7 @@ const CheckoutPage = () => {
|
|
|
474
524
|
return await response.json();
|
|
475
525
|
}
|
|
476
526
|
});
|
|
477
|
-
const freeProvisioned = checkoutQuery.data?.
|
|
527
|
+
const freeProvisioned = checkoutQuery.data?.checkoutUrl == null && checkoutQuery.data?.entitlementProvisioned === true;
|
|
478
528
|
useEffect(() => {
|
|
479
529
|
if (freeProvisioned) navigate("/account/subscriptions?checkout=success", { replace: true });
|
|
480
530
|
}, [freeProvisioned, navigate]);
|
|
@@ -716,23 +766,27 @@ const CredentialsPage = () => {
|
|
|
716
766
|
//#endregion
|
|
717
767
|
//#region src/utils/pricing-config.ts
|
|
718
768
|
function isPreviewPricingEnabled() {
|
|
719
|
-
|
|
769
|
+
if (manifestCatalog?.pricingEnabled === true) return true;
|
|
770
|
+
return zudokuConfig?.__meta?.pricingEnabled === true;
|
|
720
771
|
}
|
|
721
772
|
|
|
722
773
|
//#endregion
|
|
723
774
|
//#region src/pages/PricingPage.tsx
|
|
724
775
|
const PricingPage = () => {
|
|
725
|
-
const { pricing } = useDevPortalBillingConfig();
|
|
776
|
+
const { pricing, previewPlans } = useDevPortalBillingConfig();
|
|
726
777
|
const auth = useAuth();
|
|
727
778
|
const isPreview = useDevPortalPreviewMode();
|
|
728
|
-
const pricingEnabled = isPreviewPricingEnabled();
|
|
729
|
-
const
|
|
779
|
+
const pricingEnabled = isPreviewPricingEnabled() || isPreview;
|
|
780
|
+
const livePlans = usePlans();
|
|
781
|
+
const catalog = isPreview ? parseManifestPlans(previewPlans) : livePlans.data;
|
|
782
|
+
const isLoading = isPreview ? false : livePlans.isLoading;
|
|
783
|
+
const isError = isPreview ? false : livePlans.isError;
|
|
730
784
|
const { data: me } = useMySubscriptions();
|
|
731
785
|
if (!pricingEnabled) return /* @__PURE__ */ jsx(Navigate, {
|
|
732
786
|
to: "/",
|
|
733
787
|
replace: true
|
|
734
788
|
});
|
|
735
|
-
if (auth.isAuthenticated) return /* @__PURE__ */ jsx(Navigate, {
|
|
789
|
+
if (auth.sessionChecked && auth.isAuthenticated && !isPreview) return /* @__PURE__ */ jsx(Navigate, {
|
|
736
790
|
to: "/account/plans",
|
|
737
791
|
replace: true
|
|
738
792
|
});
|
|
@@ -765,17 +819,22 @@ const PricingPage = () => {
|
|
|
765
819
|
" ",
|
|
766
820
|
/* @__PURE__ */ jsx("code", {
|
|
767
821
|
className: "rounded bg-muted px-1.5 py-0.5",
|
|
768
|
-
children: "apitogo.json"
|
|
822
|
+
children: "apitogo.config.json"
|
|
769
823
|
}),
|
|
770
824
|
` `,
|
|
771
|
-
"(
|
|
825
|
+
"(overridable via",
|
|
772
826
|
" ",
|
|
773
827
|
/* @__PURE__ */ jsx("code", {
|
|
774
828
|
className: "rounded bg-muted px-1.5 py-0.5",
|
|
775
|
-
children: "
|
|
829
|
+
children: ".env"
|
|
776
830
|
}),
|
|
831
|
+
" with",
|
|
777
832
|
" ",
|
|
778
|
-
|
|
833
|
+
/* @__PURE__ */ jsx("code", {
|
|
834
|
+
className: "rounded bg-muted px-1.5 py-0.5",
|
|
835
|
+
children: "APITOGO_CONFIG_*"
|
|
836
|
+
}),
|
|
837
|
+
"). Checkout activates after the portal is published."
|
|
779
838
|
]
|
|
780
839
|
}) : null,
|
|
781
840
|
isLoading ? /* @__PURE__ */ jsx("p", {
|
|
@@ -800,14 +859,14 @@ const PricingPage = () => {
|
|
|
800
859
|
" ",
|
|
801
860
|
/* @__PURE__ */ jsx("code", {
|
|
802
861
|
className: "rounded bg-muted px-1.5 py-0.5",
|
|
803
|
-
children: "apitogo.json"
|
|
862
|
+
children: "apitogo.config.json"
|
|
804
863
|
}),
|
|
805
864
|
" ",
|
|
806
865
|
"and set",
|
|
807
866
|
" ",
|
|
808
867
|
/* @__PURE__ */ jsx("code", {
|
|
809
868
|
className: "rounded bg-muted px-1.5 py-0.5",
|
|
810
|
-
children: "
|
|
869
|
+
children: "modules.userPanel.plans.enabled"
|
|
811
870
|
}),
|
|
812
871
|
" ",
|
|
813
872
|
"to true."
|
|
@@ -939,8 +998,45 @@ const SubscriptionsPage = () => {
|
|
|
939
998
|
});
|
|
940
999
|
};
|
|
941
1000
|
|
|
1001
|
+
//#endregion
|
|
1002
|
+
//#region src/utils/auth-config.ts
|
|
1003
|
+
async function resolveAuthenticationNavEnabled(config) {
|
|
1004
|
+
if (config.__meta?.authenticationEnabled !== void 0) return config.__meta.authenticationEnabled;
|
|
1005
|
+
return manifestCatalog?.authenticationEnabled === true;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
//#endregion
|
|
1009
|
+
//#region src/utils/navigation-auth.ts
|
|
1010
|
+
function withoutLoginNavigation(items) {
|
|
1011
|
+
if (!items?.length) return [];
|
|
1012
|
+
return items.map((item) => {
|
|
1013
|
+
if (typeof item === "string") return item;
|
|
1014
|
+
if ("to" in item && item.to === LOGIN_NAV_PATH) return null;
|
|
1015
|
+
if (item.items?.length) {
|
|
1016
|
+
const filteredItems = withoutLoginNavigation(item.items);
|
|
1017
|
+
return {
|
|
1018
|
+
...item,
|
|
1019
|
+
items: filteredItems
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
return item;
|
|
1023
|
+
}).filter((item) => item !== null);
|
|
1024
|
+
}
|
|
1025
|
+
|
|
942
1026
|
//#endregion
|
|
943
1027
|
//#region src/utils/navigation-plans.ts
|
|
1028
|
+
function hasPricingLink(items) {
|
|
1029
|
+
return items.some((item) => typeof item !== "string" && item.type === "link" && item.to === PRICING_PATH);
|
|
1030
|
+
}
|
|
1031
|
+
function withPricingNavigation(items) {
|
|
1032
|
+
const current = items ?? [];
|
|
1033
|
+
if (hasPricingLink(current)) return current;
|
|
1034
|
+
return [...current, {
|
|
1035
|
+
type: "link",
|
|
1036
|
+
to: PRICING_PATH,
|
|
1037
|
+
label: "Pricing"
|
|
1038
|
+
}];
|
|
1039
|
+
}
|
|
944
1040
|
function withoutPricingNavigation(items) {
|
|
945
1041
|
if (!items?.length) return [];
|
|
946
1042
|
return items.map((item) => {
|
|
@@ -958,32 +1054,49 @@ function withoutPricingNavigation(items) {
|
|
|
958
1054
|
}
|
|
959
1055
|
|
|
960
1056
|
//#endregion
|
|
961
|
-
//#region src/
|
|
962
|
-
function
|
|
963
|
-
return
|
|
1057
|
+
//#region src/utils/header-navigation.ts
|
|
1058
|
+
function hasHeaderLink(navigation, path) {
|
|
1059
|
+
return navigation.some((item) => "to" in item && item.to === path);
|
|
964
1060
|
}
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
if (
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1061
|
+
function buildDevPortalHeaderNavigation({ navigation, pricingEnabled, authNavEnabled }) {
|
|
1062
|
+
let items = [...navigation ?? []];
|
|
1063
|
+
if (!pricingEnabled) items = withoutPricingNavigation(items);
|
|
1064
|
+
else if (!hasHeaderLink(items, PRICING_PATH)) items = [...items, {
|
|
1065
|
+
label: "Pricing",
|
|
1066
|
+
to: PRICING_PATH,
|
|
1067
|
+
display: "anon"
|
|
1068
|
+
}];
|
|
1069
|
+
if (!authNavEnabled) items = withoutLoginNavigation(items);
|
|
1070
|
+
else if (!hasHeaderLink(items, LOGIN_NAV_PATH)) items = [...items, {
|
|
1071
|
+
label: "Login",
|
|
1072
|
+
to: LOGIN_NAV_PATH,
|
|
1073
|
+
display: "anon"
|
|
1074
|
+
}];
|
|
1075
|
+
return items;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
//#endregion
|
|
1079
|
+
//#region src/DevPortalBillingPlugin.tsx
|
|
1080
|
+
function resolvePricingEnabled(config) {
|
|
1081
|
+
if (config.__meta?.pricingEnabled !== void 0) return config.__meta.pricingEnabled;
|
|
971
1082
|
return isPreviewPricingEnabled();
|
|
972
1083
|
}
|
|
973
1084
|
const devPortalBillingPlugin = createPlugin((options = {}) => ({
|
|
974
1085
|
transformConfig: async ({ config, merge }) => {
|
|
975
|
-
|
|
976
|
-
|
|
1086
|
+
const pricingEnabled = resolvePricingEnabled(config);
|
|
1087
|
+
const authNavEnabled = !!config.authentication && await resolveAuthenticationNavEnabled(config);
|
|
1088
|
+
const headerNavigation = buildDevPortalHeaderNavigation({
|
|
1089
|
+
navigation: config.header?.navigation,
|
|
1090
|
+
pricingEnabled,
|
|
1091
|
+
authNavEnabled
|
|
1092
|
+
});
|
|
1093
|
+
return merge({
|
|
1094
|
+
navigation: pricingEnabled ? withPricingNavigation(config.navigation) : withoutPricingNavigation(config.navigation),
|
|
977
1095
|
header: {
|
|
978
1096
|
...config.header,
|
|
979
|
-
navigation:
|
|
1097
|
+
navigation: headerNavigation
|
|
980
1098
|
}
|
|
981
1099
|
});
|
|
982
|
-
return merge({ header: { navigation: [...config.header?.navigation ?? [], {
|
|
983
|
-
label: "Pricing",
|
|
984
|
-
to: PRICING_PATH,
|
|
985
|
-
display: "anon"
|
|
986
|
-
}] } });
|
|
987
1100
|
},
|
|
988
1101
|
getProfileMenuItems: () => [{
|
|
989
1102
|
label: "My Subscriptions",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lukoweb/apitogo-plugin-dev-portal-billing",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.53",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/zuplo/zudoku",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"react": "19.2.4",
|
|
47
47
|
"react-dom": "19.2.4",
|
|
48
48
|
"tsdown": "0.20.3",
|
|
49
|
-
"@lukoweb/apitogo": "0.1.
|
|
49
|
+
"@lukoweb/apitogo": "0.1.53"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"react": ">=19.2.0",
|