@zuplo/zudoku-plugin-monetization 0.0.7 → 0.0.9
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 +9 -42
- package/dist/index.mjs +82 -77
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,47 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import * as zudoku_icons0 from "zudoku/icons";
|
|
4
|
-
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
5
|
-
import * as react0 from "react";
|
|
6
|
-
import * as zudoku_plugins0 from "zudoku/plugins";
|
|
1
|
+
import { ApiIdentityPlugin, CommonPlugin, NavigationPlugin, ProfileMenuPlugin } from "zudoku";
|
|
2
|
+
import { TransformConfigPlugin } from "zudoku/plugins";
|
|
7
3
|
|
|
8
4
|
//#region src/ZuploMonetizationPlugin.d.ts
|
|
9
5
|
type ZudokuMonetizationPluginOptions = {
|
|
10
|
-
environmentName
|
|
6
|
+
environmentName?: string;
|
|
7
|
+
pricing?: {
|
|
8
|
+
subtitle?: string;
|
|
9
|
+
title?: string;
|
|
10
|
+
};
|
|
11
11
|
};
|
|
12
|
-
declare const
|
|
13
|
-
declare const zuploMonetizationPlugin: (options: ZudokuMonetizationPluginOptions) => {
|
|
14
|
-
getIdentities: (context: zudoku0.ZudokuContext) => Promise<{
|
|
15
|
-
label: string;
|
|
16
|
-
id: string;
|
|
17
|
-
authorizeRequest: (request: Request) => Promise<Request>;
|
|
18
|
-
authorizationFields: {
|
|
19
|
-
headers: string[];
|
|
20
|
-
};
|
|
21
|
-
}[]>;
|
|
22
|
-
getProfileMenuItems: () => {
|
|
23
|
-
label: string;
|
|
24
|
-
path: string;
|
|
25
|
-
icon: react0.ForwardRefExoticComponent<Omit<zudoku_icons0.LucideProps, "ref"> & react0.RefAttributes<SVGSVGElement>>;
|
|
26
|
-
}[];
|
|
27
|
-
getRoutes: () => ({
|
|
28
|
-
Component: () => react_jsx_runtime0.JSX.Element;
|
|
29
|
-
handle: {
|
|
30
|
-
layout: string;
|
|
31
|
-
};
|
|
32
|
-
children: {
|
|
33
|
-
path: string;
|
|
34
|
-
element: react_jsx_runtime0.JSX.Element;
|
|
35
|
-
}[];
|
|
36
|
-
} | {
|
|
37
|
-
Component: () => react_jsx_runtime0.JSX.Element;
|
|
38
|
-
children: {
|
|
39
|
-
path: string;
|
|
40
|
-
element: react_jsx_runtime0.JSX.Element;
|
|
41
|
-
}[];
|
|
42
|
-
handle?: undefined;
|
|
43
|
-
})[];
|
|
44
|
-
getProtectedRoutes: () => string[];
|
|
45
|
-
} & zudoku_plugins0.TransformConfigPlugin;
|
|
12
|
+
declare const zuploMonetizationPlugin: (options?: ZudokuMonetizationPluginOptions | undefined) => NavigationPlugin & ProfileMenuPlugin & ApiIdentityPlugin & CommonPlugin & TransformConfigPlugin;
|
|
46
13
|
//#endregion
|
|
47
|
-
export {
|
|
14
|
+
export { zuploMonetizationPlugin };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn, createPlugin, joinUrl } from "zudoku";
|
|
2
|
+
import { Button, ClientOnly, Heading } from "zudoku/components";
|
|
2
3
|
import { AlertTriangleIcon, ArrowLeftIcon, ArrowUpIcon, CheckIcon, ClockIcon, InfoIcon, LockIcon, RefreshCwIcon, ShieldIcon, StarsIcon, Trash2Icon } from "zudoku/icons";
|
|
3
4
|
import { Link, Outlet, useNavigate, useParams, useSearchParams } from "zudoku/router";
|
|
4
|
-
import { Button, ClientOnly, Heading } from "zudoku/components";
|
|
5
5
|
import { useAuth, useZudoku } from "zudoku/hooks";
|
|
6
6
|
import { QueryClient, QueryClientProvider, useMutation, useQuery, useSuspenseQuery } from "zudoku/react-query";
|
|
7
7
|
import { Alert, AlertDescription, AlertTitle } from "zudoku/ui/Alert";
|
|
@@ -235,19 +235,19 @@ const formatBillingCycle = (duration) => {
|
|
|
235
235
|
if (duration.includes(" ")) return `every ${duration}`;
|
|
236
236
|
return `every ${duration}`;
|
|
237
237
|
};
|
|
238
|
-
const CheckoutConfirmPage = (
|
|
238
|
+
const CheckoutConfirmPage = () => {
|
|
239
239
|
const [search] = useSearchParams();
|
|
240
240
|
const planId = search.get("plan");
|
|
241
241
|
const zudoku = useZudoku();
|
|
242
242
|
const navigate = useNavigate();
|
|
243
|
-
const { data: plans } = usePlans(
|
|
243
|
+
const { data: plans } = usePlans(zudoku.env.ZUPLO_PUBLIC_DEPLOYMENT_NAME ?? "");
|
|
244
244
|
const selectedPlan = plans?.items?.find((plan) => plan.id === planId);
|
|
245
245
|
const rateCards = selectedPlan?.phases.at(-1)?.rateCards;
|
|
246
246
|
const { quotas, features } = categorizeRateCards(rateCards ?? []);
|
|
247
247
|
const price = selectedPlan ? getPriceFromPlan(selectedPlan) : null;
|
|
248
248
|
const billingCycle = selectedPlan?.billingCadence ? formatDuration(selectedPlan.billingCadence) : null;
|
|
249
249
|
const createSubscriptionMutation = useMutation({
|
|
250
|
-
mutationFn: createMutationFn(`/v3/zudoku-metering/${
|
|
250
|
+
mutationFn: createMutationFn(`/v3/zudoku-metering/${zudoku.env.ZUPLO_PUBLIC_DEPLOYMENT_NAME}/subscriptions`, zudoku, {
|
|
251
251
|
method: "POST",
|
|
252
252
|
body: JSON.stringify({ planId })
|
|
253
253
|
}),
|
|
@@ -402,17 +402,16 @@ const useUrlUtils = () => {
|
|
|
402
402
|
|
|
403
403
|
//#endregion
|
|
404
404
|
//#region src/pages/CheckoutPage.tsx
|
|
405
|
-
const CheckoutPage = (
|
|
406
|
-
const
|
|
405
|
+
const CheckoutPage = () => {
|
|
406
|
+
const { planId } = useParams();
|
|
407
407
|
const zudoku = useZudoku();
|
|
408
|
-
const planId = search.get("plan");
|
|
409
408
|
const auth = useAuth();
|
|
410
409
|
const { generateUrl } = useUrlUtils();
|
|
411
410
|
const { data: _data } = useQuery({
|
|
412
411
|
queryKey: ["plan", planId],
|
|
413
412
|
queryFn: async () => {
|
|
414
413
|
if (!auth.profile?.email) throw new Error("No email found for user. Make sure your Authentication Provider exposes the email address.");
|
|
415
|
-
const request = await zudoku.signRequest(new Request(`https://api.zuploedge.com/v3/zudoku-metering/${
|
|
414
|
+
const request = await zudoku.signRequest(new Request(`https://api.zuploedge.com/v3/zudoku-metering/${zudoku.env.ZUPLO_PUBLIC_DEPLOYMENT_NAME}/stripe/checkout`, {
|
|
416
415
|
method: "POST",
|
|
417
416
|
headers: { "Content-Type": "application/json" },
|
|
418
417
|
body: JSON.stringify({
|
|
@@ -472,7 +471,7 @@ var CheckoutPage_default = CheckoutPage;
|
|
|
472
471
|
|
|
473
472
|
//#endregion
|
|
474
473
|
//#region src/pages/pricing/PricingCard.tsx
|
|
475
|
-
const PricingCard = ({ plan, isPopular = false }) => {
|
|
474
|
+
const PricingCard = ({ plan, isPopular = false, disabled = false }) => {
|
|
476
475
|
const defaultPhase = plan.phases.at(-1);
|
|
477
476
|
if (!defaultPhase) return null;
|
|
478
477
|
const { quotas, features } = categorizeRateCards(defaultPhase.rateCards);
|
|
@@ -526,7 +525,7 @@ const PricingCard = ({ plan, isPopular = false }) => {
|
|
|
526
525
|
]
|
|
527
526
|
}),
|
|
528
527
|
/* @__PURE__ */ jsxs("div", {
|
|
529
|
-
className: "space-y-4 mb-6
|
|
528
|
+
className: "space-y-4 mb-6 grow",
|
|
530
529
|
children: [quotas.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
531
530
|
className: "space-y-2",
|
|
532
531
|
children: quotas.map((quota) => /* @__PURE__ */ jsx(QuotaItem, { quota }, quota.key))
|
|
@@ -535,11 +534,15 @@ const PricingCard = ({ plan, isPopular = false }) => {
|
|
|
535
534
|
children: features.map((feature) => /* @__PURE__ */ jsx(FeatureItem, { feature }, feature.key))
|
|
536
535
|
})]
|
|
537
536
|
}),
|
|
538
|
-
/* @__PURE__ */ jsx(Button, {
|
|
537
|
+
disabled ? /* @__PURE__ */ jsx(Button, {
|
|
538
|
+
disabled: true,
|
|
539
|
+
variant: isPopular ? "default" : "secondary",
|
|
540
|
+
children: "Already subscribed"
|
|
541
|
+
}) : /* @__PURE__ */ jsx(Button, {
|
|
539
542
|
variant: isPopular ? "default" : "secondary",
|
|
540
543
|
asChild: true,
|
|
541
544
|
children: /* @__PURE__ */ jsx(Link, {
|
|
542
|
-
to: `/checkout
|
|
545
|
+
to: `/checkout/${plan.id}`,
|
|
543
546
|
children: "Subscribe"
|
|
544
547
|
})
|
|
545
548
|
})
|
|
@@ -549,17 +552,14 @@ const PricingCard = ({ plan, isPopular = false }) => {
|
|
|
549
552
|
|
|
550
553
|
//#endregion
|
|
551
554
|
//#region src/pages/PricingPage.tsx
|
|
552
|
-
const PricingPage = ({
|
|
553
|
-
const
|
|
554
|
-
const
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
];
|
|
561
|
-
const sortedPlans = [...pricingTableData.items].sort((a, b) => {
|
|
562
|
-
return planOrder.indexOf(a.key) - planOrder.indexOf(b.key);
|
|
555
|
+
const PricingPage = ({ subtitle = "See our pricing options and choose the one that best suits your needs.", title = "Pricing" }) => {
|
|
556
|
+
const zudoku = useZudoku();
|
|
557
|
+
const { data: pricingTable } = useSuspenseQuery({ queryKey: [`/v3/zudoku-metering/${zudoku.env.ZUPLO_PUBLIC_DEPLOYMENT_NAME}/pricing-page`] });
|
|
558
|
+
const auth = useAuth();
|
|
559
|
+
const { data: subscriptions = { items: [] } } = useQuery({
|
|
560
|
+
meta: { context: zudoku },
|
|
561
|
+
queryKey: [`/v3/zudoku-metering/${zudoku.env.ZUPLO_PUBLIC_DEPLOYMENT_NAME}/subscriptions`],
|
|
562
|
+
enabled: auth.isAuthenticated
|
|
563
563
|
});
|
|
564
564
|
const getGridCols = (count) => {
|
|
565
565
|
if (count === 1) return "lg:grid-cols-1";
|
|
@@ -574,18 +574,19 @@ const PricingPage = ({ environmentName }) => {
|
|
|
574
574
|
className: "text-center mb-12",
|
|
575
575
|
children: [/* @__PURE__ */ jsx(Heading, {
|
|
576
576
|
level: 1,
|
|
577
|
-
children:
|
|
577
|
+
children: title
|
|
578
578
|
}), /* @__PURE__ */ jsx("p", {
|
|
579
579
|
className: "text-lg text-gray-600 dark:text-gray-400",
|
|
580
|
-
children:
|
|
580
|
+
children: subtitle
|
|
581
581
|
})]
|
|
582
582
|
}), /* @__PURE__ */ jsx("div", {
|
|
583
583
|
className: "flex justify-center",
|
|
584
584
|
children: /* @__PURE__ */ jsx("div", {
|
|
585
|
-
className: cn("w-full md:w-auto grid grid-cols-1 md:grid-cols-2 gap-6 md:max-w-fit", getGridCols(
|
|
586
|
-
children:
|
|
585
|
+
className: cn("w-full md:w-auto grid grid-cols-1 md:grid-cols-2 gap-6 md:max-w-fit", getGridCols(pricingTable?.items?.length ?? 0)),
|
|
586
|
+
children: pricingTable?.items?.map((plan) => /* @__PURE__ */ jsx(PricingCard, {
|
|
587
587
|
plan,
|
|
588
|
-
isPopular: plan.key === "pro"
|
|
588
|
+
isPopular: plan.key === "pro",
|
|
589
|
+
disabled: subscriptions.items.some((subscription) => subscription.plan.id === plan.id)
|
|
589
590
|
}, plan.id))
|
|
590
591
|
})
|
|
591
592
|
})]
|
|
@@ -949,7 +950,6 @@ const Usage = ({ subscriptionId, environmentName, currentItems }) => {
|
|
|
949
950
|
queryKey: [`/v3/zudoku-metering/${environmentName}/subscriptions/${subscriptionId}/usage`],
|
|
950
951
|
meta: { context: zudoku }
|
|
951
952
|
});
|
|
952
|
-
console.log("currentItems", currentItems);
|
|
953
953
|
return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Heading, {
|
|
954
954
|
level: 3,
|
|
955
955
|
className: "mb-4",
|
|
@@ -965,8 +965,9 @@ const Usage = ({ subscriptionId, environmentName, currentItems }) => {
|
|
|
965
965
|
|
|
966
966
|
//#endregion
|
|
967
967
|
//#region src/pages/SubscriptionsPage.tsx
|
|
968
|
-
const SubscriptionsPage = (
|
|
969
|
-
const
|
|
968
|
+
const SubscriptionsPage = () => {
|
|
969
|
+
const deploymentName = useZudoku().env.ZUPLO_PUBLIC_DEPLOYMENT_NAME ?? "";
|
|
970
|
+
const { data } = useSubscriptions(deploymentName);
|
|
970
971
|
const { subscriptionId } = useParams();
|
|
971
972
|
const subscriptions = data?.items ?? [];
|
|
972
973
|
const activeSubscription = useMemo(() => {
|
|
@@ -975,7 +976,6 @@ const SubscriptionsPage = ({ environmentName }) => {
|
|
|
975
976
|
return subscriptions[0];
|
|
976
977
|
}, [subscriptions, subscriptionId]);
|
|
977
978
|
const activePhase = activeSubscription?.phases.find((p) => new Date(p.activeFrom) <= /* @__PURE__ */ new Date() && (!p.activeTo || new Date(p.activeTo) >= /* @__PURE__ */ new Date()));
|
|
978
|
-
console.log("activePhase", activePhase);
|
|
979
979
|
return /* @__PURE__ */ jsx("div", {
|
|
980
980
|
className: "w-full py-12",
|
|
981
981
|
children: /* @__PURE__ */ jsxs("div", {
|
|
@@ -994,11 +994,11 @@ const SubscriptionsPage = ({ environmentName }) => {
|
|
|
994
994
|
}),
|
|
995
995
|
activeSubscription && /* @__PURE__ */ jsx(Usage, {
|
|
996
996
|
subscriptionId: activeSubscription?.id,
|
|
997
|
-
environmentName,
|
|
997
|
+
environmentName: deploymentName,
|
|
998
998
|
currentItems: activePhase?.items
|
|
999
999
|
}),
|
|
1000
1000
|
activeSubscription?.consumer?.apiKeys && /* @__PURE__ */ jsx(ApiKeysList, {
|
|
1001
|
-
deploymentName
|
|
1001
|
+
deploymentName,
|
|
1002
1002
|
consumerId: activeSubscription.consumer.id,
|
|
1003
1003
|
apiKeys: activeSubscription.consumer.apiKeys
|
|
1004
1004
|
}),
|
|
@@ -1018,28 +1018,28 @@ var SubscriptionsPage_default = SubscriptionsPage;
|
|
|
1018
1018
|
//#endregion
|
|
1019
1019
|
//#region src/ZuploMonetizationPlugin.tsx
|
|
1020
1020
|
const PRICING_PATH = "/pricing";
|
|
1021
|
-
const enableMonetization = (config, options) => {
|
|
1022
|
-
return {
|
|
1023
|
-
...config,
|
|
1024
|
-
plugins: [...config.plugins ?? [], zuploMonetizationPlugin(options)],
|
|
1025
|
-
slots: { "head-navigation-start": () => {
|
|
1026
|
-
return /* @__PURE__ */ jsx(Link, {
|
|
1027
|
-
to: PRICING_PATH,
|
|
1028
|
-
children: "Pricing"
|
|
1029
|
-
});
|
|
1030
|
-
} }
|
|
1031
|
-
};
|
|
1032
|
-
};
|
|
1033
1021
|
const zuploMonetizationPlugin = createPlugin((options) => ({
|
|
1022
|
+
transformConfig: ({ merge }) => merge({ slots: { "head-navigation-start": () => /* @__PURE__ */ jsx(Button, {
|
|
1023
|
+
asChild: true,
|
|
1024
|
+
variant: "ghost",
|
|
1025
|
+
children: /* @__PURE__ */ jsx(Link, {
|
|
1026
|
+
to: PRICING_PATH,
|
|
1027
|
+
children: "Pricing"
|
|
1028
|
+
})
|
|
1029
|
+
}) } }),
|
|
1030
|
+
initialize: (context) => {
|
|
1031
|
+
if (!context.env.ZUPLO_PUBLIC_DEPLOYMENT_NAME || options?.environmentName) throw new Error("ZUPLO_PUBLIC_DEPLOYMENT_NAME is not set");
|
|
1032
|
+
},
|
|
1034
1033
|
getIdentities: async (context) => {
|
|
1035
1034
|
return (await queryClient.fetchQuery({
|
|
1036
|
-
queryKey: [`/v3/zudoku-metering/${
|
|
1035
|
+
queryKey: [`/v3/zudoku-metering/${context.env.ZUPLO_PUBLIC_DEPLOYMENT_NAME}/subscriptions`],
|
|
1037
1036
|
meta: { context }
|
|
1038
1037
|
})).items.flatMap((item) => item.consumer.apiKeys.map((apiKey) => ({
|
|
1039
1038
|
label: item.name,
|
|
1040
1039
|
id: apiKey.id,
|
|
1041
1040
|
authorizeRequest: async (request) => {
|
|
1042
|
-
|
|
1041
|
+
request.headers.set("Authorization", `Bearer ${apiKey.key}`);
|
|
1042
|
+
return request;
|
|
1043
1043
|
},
|
|
1044
1044
|
authorizationFields: { headers: ["Authorization"] }
|
|
1045
1045
|
})));
|
|
@@ -1049,41 +1049,46 @@ const zuploMonetizationPlugin = createPlugin((options) => ({
|
|
|
1049
1049
|
path: "/subscriptions",
|
|
1050
1050
|
icon: StarsIcon
|
|
1051
1051
|
}],
|
|
1052
|
-
getRoutes: () =>
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1052
|
+
getRoutes: () => {
|
|
1053
|
+
return [{
|
|
1054
|
+
Component: ZuploMonetizationWrapper_default,
|
|
1055
|
+
handle: { layout: "none" },
|
|
1056
|
+
children: [{
|
|
1057
|
+
path: "/checkout/:planId?",
|
|
1058
|
+
element: /* @__PURE__ */ jsx(CheckoutPage_default, {})
|
|
1059
|
+
}, {
|
|
1060
|
+
path: "/checkout-confirm",
|
|
1061
|
+
element: /* @__PURE__ */ jsx(CheckoutConfimPage_default, {})
|
|
1062
|
+
}]
|
|
1058
1063
|
}, {
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
]
|
|
1078
|
-
}
|
|
1064
|
+
Component: ZuploMonetizationWrapper_default,
|
|
1065
|
+
children: [
|
|
1066
|
+
{
|
|
1067
|
+
path: "/pricing",
|
|
1068
|
+
element: /* @__PURE__ */ jsx(PricingPage_default, {
|
|
1069
|
+
subtitle: options?.pricing?.subtitle,
|
|
1070
|
+
title: options?.pricing?.title
|
|
1071
|
+
})
|
|
1072
|
+
},
|
|
1073
|
+
{
|
|
1074
|
+
path: "/checkout-failed",
|
|
1075
|
+
element: /* @__PURE__ */ jsx(CheckoutFailedPage_default, {})
|
|
1076
|
+
},
|
|
1077
|
+
{
|
|
1078
|
+
path: "/subscriptions/:subscriptionId?",
|
|
1079
|
+
element: /* @__PURE__ */ jsx(SubscriptionsPage_default, {})
|
|
1080
|
+
}
|
|
1081
|
+
]
|
|
1082
|
+
}];
|
|
1083
|
+
},
|
|
1079
1084
|
getProtectedRoutes: () => {
|
|
1080
1085
|
return [
|
|
1081
|
-
"/checkout",
|
|
1086
|
+
"/checkout/*",
|
|
1082
1087
|
"/checkout-success",
|
|
1083
1088
|
"/checkout-failed"
|
|
1084
1089
|
];
|
|
1085
1090
|
}
|
|
1086
|
-
})
|
|
1091
|
+
}));
|
|
1087
1092
|
|
|
1088
1093
|
//#endregion
|
|
1089
|
-
export {
|
|
1094
|
+
export { zuploMonetizationPlugin };
|