@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 CHANGED
@@ -1,47 +1,14 @@
1
- import * as zudoku0 from "zudoku";
2
- import { ZudokuConfig } from "zudoku";
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: string;
6
+ environmentName?: string;
7
+ pricing?: {
8
+ subtitle?: string;
9
+ title?: string;
10
+ };
11
11
  };
12
- declare const enableMonetization: (config: ZudokuConfig, options: ZudokuMonetizationPluginOptions) => ZudokuConfig;
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 { enableMonetization, zuploMonetizationPlugin };
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 = ({ environmentName }) => {
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(environmentName);
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/${environmentName}/subscriptions`, zudoku, {
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 = ({ environmentName }) => {
406
- const [search] = useSearchParams();
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/${environmentName}/stripe/checkout`, {
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 flex-grow",
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?plan=${plan.id}`,
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 = ({ environmentName }) => {
553
- const { data: pricingTableData } = useSuspenseQuery({ queryKey: [`/v3/zudoku-metering/${environmentName}/pricing-page`] });
554
- const planOrder = [
555
- "developer",
556
- "startup",
557
- "pro",
558
- "business",
559
- "enterprise"
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: "Pricing"
577
+ children: title
578
578
  }), /* @__PURE__ */ jsx("p", {
579
579
  className: "text-lg text-gray-600 dark:text-gray-400",
580
- children: "Global live music data, flexible plans for every scale"
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(sortedPlans.length)),
586
- children: sortedPlans.map((plan) => /* @__PURE__ */ jsx(PricingCard, {
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 = ({ environmentName }) => {
969
- const { data } = useSubscriptions(environmentName);
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: environmentName,
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/${options.environmentName}/subscriptions`],
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
- return new Request(request, { headers: { Authorization: `Bearer ${apiKey.key}` } });
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
- Component: ZuploMonetizationWrapper_default,
1054
- handle: { layout: "none" },
1055
- children: [{
1056
- path: "/checkout",
1057
- element: /* @__PURE__ */ jsx(CheckoutPage_default, { environmentName: options.environmentName })
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
- path: "/checkout-confirm",
1060
- element: /* @__PURE__ */ jsx(CheckoutConfimPage_default, { environmentName: options.environmentName })
1061
- }]
1062
- }, {
1063
- Component: ZuploMonetizationWrapper_default,
1064
- children: [
1065
- {
1066
- path: "/pricing",
1067
- element: /* @__PURE__ */ jsx(PricingPage_default, { environmentName: options.environmentName })
1068
- },
1069
- {
1070
- path: "/checkout-failed",
1071
- element: /* @__PURE__ */ jsx(CheckoutFailedPage_default, {})
1072
- },
1073
- {
1074
- path: "/subscriptions/:subscriptionId?",
1075
- element: /* @__PURE__ */ jsx(SubscriptionsPage_default, { environmentName: options.environmentName })
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
- }), import.meta.url);
1091
+ }));
1087
1092
 
1088
1093
  //#endregion
1089
- export { enableMonetization, zuploMonetizationPlugin };
1094
+ export { zuploMonetizationPlugin };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zuplo/zudoku-plugin-monetization",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "type": "module",
5
5
  "main": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.mts",