@zuplo/zudoku-plugin-monetization 0.0.4 → 0.0.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/index.d.mts CHANGED
@@ -1,10 +1,47 @@
1
- import { ZudokuConfig, ZudokuPlugin } from "zudoku";
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";
2
7
 
3
8
  //#region src/ZuploMonetizationPlugin.d.ts
4
9
  type ZudokuMonetizationPluginOptions = {
5
10
  environmentName: string;
6
11
  };
7
12
  declare const enableMonetization: (config: ZudokuConfig, options: ZudokuMonetizationPluginOptions) => ZudokuConfig;
8
- declare const zuploMonetizationPlugin: (options: ZudokuMonetizationPluginOptions) => ZudokuPlugin;
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;
9
46
  //#endregion
10
47
  export { enableMonetization, zuploMonetizationPlugin };
package/dist/index.mjs CHANGED
@@ -1,18 +1,21 @@
1
- import { ArrowLeftIcon, CheckIcon, ClockIcon, InfoIcon, LockIcon, RefreshCwIcon, ShieldIcon, StarsIcon, Trash2Icon } from "zudoku/icons";
1
+ import { cn, createPlugin, joinUrl } from "zudoku";
2
+ import { AlertTriangleIcon, ArrowLeftIcon, ArrowUpIcon, CheckIcon, ClockIcon, InfoIcon, LockIcon, RefreshCwIcon, ShieldIcon, StarsIcon, Trash2Icon } from "zudoku/icons";
2
3
  import { Link, Outlet, useNavigate, useParams, useSearchParams } from "zudoku/router";
3
- import { Button, Heading } from "zudoku/components";
4
+ import { Button, ClientOnly, Heading } from "zudoku/components";
4
5
  import { useAuth, useZudoku } from "zudoku/hooks";
5
6
  import { QueryClient, QueryClientProvider, useMutation, useQuery, useSuspenseQuery } from "zudoku/react-query";
6
7
  import { Alert, AlertDescription, AlertTitle } from "zudoku/ui/Alert";
7
- import { Card, CardContent, CardHeader, CardTitle } from "zudoku/ui/Card";
8
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "zudoku/ui/Card";
8
9
  import { Separator } from "zudoku/ui/Separator";
9
- import { cn, joinUrl } from "zudoku";
10
10
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
11
  import { parse } from "tinyduration";
12
12
  import { useMemo } from "react";
13
+ import { ActionButton } from "zudoku/ui/ActionButton";
13
14
  import { Button as Button$1 } from "zudoku/ui/Button";
14
15
  import { Secret } from "zudoku/ui/Secret";
15
- import { Item, ItemContent, ItemTitle } from "zudoku/ui/Item";
16
+ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "zudoku/ui/AlertDialog";
17
+ import { Badge } from "zudoku/ui/Badge";
18
+ import { Item, ItemContent, ItemDescription, ItemTitle } from "zudoku/ui/Item";
16
19
  import { Progress } from "zudoku/ui/Progress";
17
20
 
18
21
  //#region src/components/FeatureItem.tsx
@@ -86,6 +89,22 @@ const formatDuration = (iso) => {
86
89
  return iso;
87
90
  }
88
91
  };
92
+ const formatDurationInterval = (iso) => {
93
+ try {
94
+ const d = parse(iso);
95
+ if (d.years === 1) return "yearly";
96
+ if (d.years && d.years > 1) return `every ${d.years} years`;
97
+ if (d.months === 1) return "monthly";
98
+ if (d.months && d.months > 1) return `every ${d.months} months`;
99
+ if (d.weeks === 1) return "weekly";
100
+ if (d.weeks && d.weeks > 1) return `every ${d.weeks} weeks`;
101
+ if (d.days === 1) return "daily";
102
+ if (d.days && d.days > 1) return `every ${d.days} days`;
103
+ return iso;
104
+ } catch {
105
+ return iso;
106
+ }
107
+ };
89
108
 
90
109
  //#endregion
91
110
  //#region src/utils/categorizeRateCards.ts
@@ -201,7 +220,7 @@ const queryClient = new QueryClient({ defaultOptions: {
201
220
  const ZuploMonetizationWrapper = () => {
202
221
  return /* @__PURE__ */ jsx(QueryClientProvider, {
203
222
  client: queryClient,
204
- children: /* @__PURE__ */ jsx(Outlet, {})
223
+ children: /* @__PURE__ */ jsx(ClientOnly, { children: /* @__PURE__ */ jsx(Outlet, {}) })
205
224
  });
206
225
  };
207
226
  var ZuploMonetizationWrapper_default = ZuploMonetizationWrapper;
@@ -336,9 +355,13 @@ const CheckoutConfirmPage = ({ environmentName }) => {
336
355
  disabled: createSubscriptionMutation.isPending,
337
356
  children: createSubscriptionMutation.isPending ? "Processing Payment..." : "Confirm & Subscribe"
338
357
  }), /* @__PURE__ */ jsx(Button, {
339
- variant: "outline",
358
+ variant: "ghost",
340
359
  className: "w-full",
341
- asChild: true
360
+ asChild: true,
361
+ children: /* @__PURE__ */ jsx(Link, {
362
+ to: "/pricing",
363
+ children: "Cancel"
364
+ })
342
365
  })]
343
366
  }),
344
367
  /* @__PURE__ */ jsx("div", {
@@ -579,10 +602,23 @@ const useSubscriptions = (environmentName) => {
579
602
  });
580
603
  };
581
604
 
605
+ //#endregion
606
+ //#region src/pages/subscriptions/ConfirmDeleteKeyAlert.tsx
607
+ const ConfirmDeleteKeyAlert = ({ children, onDelete }) => {
608
+ return /* @__PURE__ */ jsxs(AlertDialog, { children: [/* @__PURE__ */ jsx(AlertDialogTrigger, {
609
+ asChild: true,
610
+ children
611
+ }), /* @__PURE__ */ jsxs(AlertDialogContent, { children: [/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [/* @__PURE__ */ jsx(AlertDialogTitle, { children: "Are you absolutely sure?" }), /* @__PURE__ */ jsx(AlertDialogDescription, { children: "This action cannot be undone. This will permanently delete your API key." })] }), /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [/* @__PURE__ */ jsx(AlertDialogCancel, { children: "Cancel" }), /* @__PURE__ */ jsx(AlertDialogAction, {
612
+ onClick: onDelete,
613
+ children: "Continue"
614
+ })] })] })] });
615
+ };
616
+ var ConfirmDeleteKeyAlert_default = ConfirmDeleteKeyAlert;
617
+
582
618
  //#endregion
583
619
  //#region src/pages/subscriptions/ApiKey.tsx
584
620
  const ApiKey = ({ apiKey, createdAt, lastUsed, expiresOn, isActive = true, label, onDelete }) => {
585
- const formatDate = (dateString) => {
621
+ const formatDate$1 = (dateString) => {
586
622
  if (!dateString) return "";
587
623
  return new Date(dateString).toLocaleDateString("en-US", {
588
624
  month: "short",
@@ -602,12 +638,12 @@ const ApiKey = ({ apiKey, createdAt, lastUsed, expiresOn, isActive = true, label
602
638
  const diffInDays = Math.floor(diffInHours / 24);
603
639
  if (diffInDays === 1) return "1 day ago";
604
640
  if (diffInDays < 30) return `${diffInDays} days ago`;
605
- return formatDate(dateString);
641
+ return formatDate$1(dateString);
606
642
  };
607
643
  const isExpiring = expiresOn && new Date(expiresOn) < new Date(Date.now() + 720 * 60 * 60 * 1e3);
608
644
  const isExpired = expiresOn && new Date(expiresOn) < /* @__PURE__ */ new Date();
609
645
  return /* @__PURE__ */ jsx(Card, {
610
- className: isExpiring && !isExpired ? "border-yellow-200 bg-yellow-50" : "",
646
+ className: isExpiring && !isExpired ? "border-amber-200 bg-amber-50 dark:bg-amber-900/50 dark:border-amber-800" : "",
611
647
  children: /* @__PURE__ */ jsx(CardContent, {
612
648
  className: "p-6",
613
649
  children: /* @__PURE__ */ jsxs("div", {
@@ -624,7 +660,7 @@ const ApiKey = ({ apiKey, createdAt, lastUsed, expiresOn, isActive = true, label
624
660
  className: "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800",
625
661
  children: "Active"
626
662
  }) : /* @__PURE__ */ jsx("span", {
627
- className: "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800",
663
+ className: "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-amber-100 text-amber-800",
628
664
  children: "Expiring"
629
665
  })]
630
666
  })
@@ -634,27 +670,32 @@ const ApiKey = ({ apiKey, createdAt, lastUsed, expiresOn, isActive = true, label
634
670
  children: [
635
671
  /* @__PURE__ */ jsxs("div", {
636
672
  className: "flex items-center gap-1.5",
637
- children: [/* @__PURE__ */ jsx(ClockIcon, { className: "size-3.5" }), /* @__PURE__ */ jsxs("span", { children: ["Created ", formatDate(createdAt)] })]
673
+ children: [/* @__PURE__ */ jsx(ClockIcon, { className: "size-3.5" }), /* @__PURE__ */ jsxs("span", { children: ["Created ", formatDate$1(createdAt)] })]
638
674
  }),
639
675
  /* @__PURE__ */ jsx("span", { children: "•" }),
640
676
  /* @__PURE__ */ jsxs("span", { children: ["Last used ", getTimeAgo(lastUsed)] }),
641
677
  expiresOn && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", { children: "•" }), /* @__PURE__ */ jsxs("span", {
642
- className: isExpired ? "text-red-700 font-medium" : isExpiring ? "text-yellow-700 font-medium" : "",
643
- children: ["Expires ", formatDate(expiresOn)]
678
+ className: isExpired ? "text-red-700 font-medium" : isExpiring ? "text-amber-700 font-medium" : "",
679
+ children: ["Expires ", formatDate$1(expiresOn)]
644
680
  })] })
645
681
  ]
646
682
  }),
647
683
  /* @__PURE__ */ jsxs("div", {
648
- className: "flex items-center gap-2 rounded-md font-mono text-sm",
684
+ className: "flex items-center gap-2 rounded-md font-mono text-sm ",
649
685
  children: [/* @__PURE__ */ jsx(Secret, {
650
686
  secret: apiKey,
651
- status: isActive ? "active" : "expiring"
652
- }), !isActive && /* @__PURE__ */ jsx("button", {
653
- onClick: onDelete,
654
- className: "text-red-500 hover:text-red-700 p-1",
655
- type: "button",
656
- "aria-label": "Delete API key",
657
- children: /* @__PURE__ */ jsx(Trash2Icon, { className: "size-4" })
687
+ status: isActive ? "active" : "expiring",
688
+ className: cn((isExpired || isExpiring) && "bg-amber-100 dark:bg-amber-900 border border-amber-200 dark:border-amber-800")
689
+ }), !isActive && /* @__PURE__ */ jsx(ConfirmDeleteKeyAlert_default, {
690
+ onDelete,
691
+ children: /* @__PURE__ */ jsx(Button$1, {
692
+ className: "text-red-500 hover:text-red-700 p-1",
693
+ type: "button",
694
+ "aria-label": "Delete API key",
695
+ variant: "ghost",
696
+ size: "icon-sm",
697
+ children: /* @__PURE__ */ jsx(Trash2Icon, { className: "size-4" })
698
+ })
658
699
  })]
659
700
  })
660
701
  ]
@@ -688,6 +729,19 @@ const ApiKeyInfo = () => {
688
729
  });
689
730
  };
690
731
 
732
+ //#endregion
733
+ //#region src/pages/subscriptions/ConfirmRollKeyAlert.tsx
734
+ const ConfirmRollKeyAlert = ({ children, onRollKey }) => {
735
+ return /* @__PURE__ */ jsxs(AlertDialog, { children: [/* @__PURE__ */ jsx(AlertDialogTrigger, {
736
+ asChild: true,
737
+ children
738
+ }), /* @__PURE__ */ jsxs(AlertDialogContent, { children: [/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [/* @__PURE__ */ jsx(AlertDialogTitle, { children: "Are you absolutely sure?" }), /* @__PURE__ */ jsx(AlertDialogDescription, { children: "This will roll your API key and create a new one. You will need to update use the new key. The old key will be deleted after 7 days." })] }), /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [/* @__PURE__ */ jsx(AlertDialogCancel, { children: "Cancel" }), /* @__PURE__ */ jsx(AlertDialogAction, {
739
+ onClick: onRollKey,
740
+ children: "Continue"
741
+ })] })] })] });
742
+ };
743
+ var ConfirmRollKeyAlert_default = ConfirmRollKeyAlert;
744
+
691
745
  //#endregion
692
746
  //#region src/pages/subscriptions/ApiKeysList.tsx
693
747
  const ApiKeysList = ({ apiKeys, deploymentName, consumerId }) => {
@@ -727,10 +781,16 @@ const ApiKeysList = ({ apiKeys, deploymentName, consumerId }) => {
727
781
  children: [/* @__PURE__ */ jsx("h3", {
728
782
  className: "text-lg font-semibold",
729
783
  children: "API Keys"
730
- }), /* @__PURE__ */ jsxs(Button$1, {
731
- onClick: () => rollKeyMutation.mutateAsync(),
732
- disabled: rollKeyMutation.isPending,
733
- children: [/* @__PURE__ */ jsx(RefreshCwIcon, { className: `size-4 ${rollKeyMutation.isPending ? "animate-spin" : ""}` }), rollKeyMutation.isPending ? "Rolling..." : "Roll API Key"]
784
+ }), /* @__PURE__ */ jsx(ConfirmRollKeyAlert_default, {
785
+ onRollKey: () => rollKeyMutation.mutateAsync(),
786
+ children: /* @__PURE__ */ jsx(ActionButton, {
787
+ isPending: rollKeyMutation.isPending,
788
+ variant: "outline",
789
+ children: /* @__PURE__ */ jsxs("div", {
790
+ className: "flex items-center gap-2",
791
+ children: [/* @__PURE__ */ jsx(RefreshCwIcon, { className: `size-4 ${rollKeyMutation.isPending ? "animate-spin" : ""}` }), "Roll API Key"]
792
+ })
793
+ })
734
794
  })]
735
795
  }),
736
796
  deleteKeyMutation.error && /* @__PURE__ */ jsxs(Alert, {
@@ -773,6 +833,13 @@ const ApiKeysList = ({ apiKeys, deploymentName, consumerId }) => {
773
833
 
774
834
  //#endregion
775
835
  //#region src/pages/subscriptions/SubscriptionsList.tsx
836
+ const formatDate = (dateString) => {
837
+ return new Date(dateString).toLocaleDateString("en-US", {
838
+ month: "short",
839
+ day: "numeric",
840
+ year: "numeric"
841
+ });
842
+ };
776
843
  const SubscriptionsList = ({ subscriptions, activeSubscriptionId }) => {
777
844
  return /* @__PURE__ */ jsx("div", {
778
845
  className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3",
@@ -783,8 +850,11 @@ const SubscriptionsList = ({ subscriptions, activeSubscriptionId }) => {
783
850
  children: /* @__PURE__ */ jsx(Item, {
784
851
  size: "sm",
785
852
  variant: "outline",
786
- className: cn(isActive && "border-primary bg-primary/5 shadow-md"),
787
- children: /* @__PURE__ */ jsx(ItemContent, { children: /* @__PURE__ */ jsx(ItemTitle, { children: subscription.name }) })
853
+ className: cn(isActive && "border-primary bg-primary/5"),
854
+ children: /* @__PURE__ */ jsxs(ItemContent, { children: [/* @__PURE__ */ jsxs(ItemTitle, { children: [subscription.name, /* @__PURE__ */ jsx(Badge, {
855
+ className: "capitalize",
856
+ children: formatDurationInterval(subscription.billingCadence)
857
+ })] }), /* @__PURE__ */ jsxs(ItemDescription, { children: ["Started: ", formatDate(subscription.activeFrom)] })] })
788
858
  }, subscription.id)
789
859
  }, subscription.id);
790
860
  })
@@ -796,49 +866,98 @@ const SubscriptionsList = ({ subscriptions, activeSubscriptionId }) => {
796
866
  const isMeteredEntitlement = (entitlement) => {
797
867
  return "balance" in entitlement;
798
868
  };
799
- const Usage = ({ subscriptionId, environmentName }) => {
800
- const zudoku = useZudoku();
801
- const { data: plans } = usePlans(environmentName);
802
- const { data: usage } = useSuspenseQuery({
803
- queryKey: [`/v3/zudoku-metering/${environmentName}/subscriptions/${subscriptionId}/usage`],
804
- meta: { context: zudoku }
805
- });
806
- return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Heading, {
807
- level: 3,
808
- className: "mb-4",
809
- children: "Usage"
810
- }), /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(CardContent, {
811
- className: "p-6",
812
- children: Object.entries(usage.entitlements).filter((entry) => isMeteredEntitlement(entry[1])).map(([key, metric], index) => /* @__PURE__ */ jsxs("div", { children: [index > 0 && /* @__PURE__ */ jsx("div", { className: "my-4 border-t" }), /* @__PURE__ */ jsxs("div", {
813
- className: "space-y-2",
869
+ const UsageItem = ({ meter, item }) => {
870
+ return /* @__PURE__ */ jsxs(Card, {
871
+ className: cn(meter.overage > 0 && "border-red-400 bg-red-50/50"),
872
+ children: [/* @__PURE__ */ jsxs(CardHeader, {
873
+ className: cn("pb-2"),
874
+ children: [
875
+ meter.overage > 0 && /* @__PURE__ */ jsxs("div", {
876
+ className: "flex items-start gap-3 p-3 bg-red-100 rounded-lg mb-4",
877
+ children: [
878
+ /* @__PURE__ */ jsx(AlertTriangleIcon, { className: "size-5 text-red-600 shrink-0 mt-0.5" }),
879
+ /* @__PURE__ */ jsxs("div", {
880
+ className: "flex-1",
881
+ children: [/* @__PURE__ */ jsx("p", {
882
+ className: "text-sm font-medium text-red-800",
883
+ children: "You've exceeded your monthly quota"
884
+ }), /* @__PURE__ */ jsx("p", {
885
+ className: "text-xs text-red-700 mt-0.5",
886
+ children: "Additional API calls are being charged at the overage rate ($0.03/call). Upgrade to Enterprise for unlimited calls."
887
+ })]
888
+ }),
889
+ /* @__PURE__ */ jsx(Button, {
890
+ variant: "destructive",
891
+ size: "sm",
892
+ asChild: true,
893
+ children: /* @__PURE__ */ jsxs(Link, {
894
+ to: "/pricing",
895
+ children: [/* @__PURE__ */ jsx(ArrowUpIcon, {}), "Upgrade"]
896
+ })
897
+ })
898
+ ]
899
+ }),
900
+ /* @__PURE__ */ jsxs(CardTitle, { children: [
901
+ item.name,
902
+ " ",
903
+ item.price?.amount
904
+ ] }),
905
+ /* @__PURE__ */ jsx(CardDescription, {})
906
+ ]
907
+ }), /* @__PURE__ */ jsxs(CardContent, {
908
+ className: "pace-y-2",
814
909
  children: [
815
910
  /* @__PURE__ */ jsxs("div", {
816
911
  className: "flex items-center justify-between text-sm",
817
- children: [/* @__PURE__ */ jsxs("div", {
818
- className: "flex flex-col gap-2",
819
- children: [/* @__PURE__ */ jsx("span", {
820
- className: "text-base font-medium capitalize",
821
- children: key
822
- }), /* @__PURE__ */ jsxs("span", {
823
- className: "text-muted-foreground",
824
- children: [metric.usage.toLocaleString(), " used"]
825
- })]
912
+ children: [/* @__PURE__ */ jsx("div", {
913
+ className: "flex flex-col gap-2 mb-2",
914
+ children: /* @__PURE__ */ jsxs("span", {
915
+ className: cn(meter.overage > 0 && "text-red-600 font-medium"),
916
+ children: [
917
+ meter.usage.toLocaleString(),
918
+ " used",
919
+ meter.overage > 0 && /* @__PURE__ */ jsxs("span", {
920
+ className: "ml-1 text-xs",
921
+ children: [
922
+ "(+",
923
+ meter.overage.toLocaleString(),
924
+ " overage)"
925
+ ]
926
+ })
927
+ ]
928
+ })
826
929
  }), /* @__PURE__ */ jsxs("span", {
827
- className: "text-muted-foreground",
828
- children: [metric.balance.toLocaleString(), " limit"]
930
+ className: "text-foreground font-medium",
931
+ children: [meter.balance.toLocaleString(), " limit"]
829
932
  })]
830
933
  }),
831
934
  /* @__PURE__ */ jsx(Progress, {
832
- value: metric.usage / metric.balance * 100,
833
- className: "h-2"
935
+ value: meter.usage / meter.balance * 100,
936
+ className: cn("mb-3 h-2", meter.overage > 0 && "bg-red-500")
834
937
  }),
835
938
  /* @__PURE__ */ jsxs("p", {
836
- className: "text-sm text-muted-foreground",
837
- children: [metric.balance - metric.usage, " calls remaining this month"]
939
+ className: "text-xs text-muted-foreground",
940
+ children: [(meter.balance - meter.usage).toLocaleString(), " remaining this month"]
838
941
  })
839
942
  ]
840
- })] }, key))
841
- }) })] });
943
+ })]
944
+ });
945
+ };
946
+ const Usage = ({ subscriptionId, environmentName, currentItems }) => {
947
+ const zudoku = useZudoku();
948
+ const { data: usage } = useSuspenseQuery({
949
+ queryKey: [`/v3/zudoku-metering/${environmentName}/subscriptions/${subscriptionId}/usage`],
950
+ meta: { context: zudoku }
951
+ });
952
+ const meteredEntitlements = currentItems.filter((item) => item.included.entitlement?.type === "metered");
953
+ return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Heading, {
954
+ level: 3,
955
+ className: "mb-4",
956
+ children: "Usage"
957
+ }), Object.entries(usage.entitlements).filter((entry) => isMeteredEntitlement(entry[1])).map(([key, metric]) => /* @__PURE__ */ jsx(UsageItem, {
958
+ meter: { ...metric },
959
+ item: meteredEntitlements.find((item) => item.included.entitlement?.featureKey === key)
960
+ }, key))] });
842
961
  };
843
962
 
844
963
  //#endregion
@@ -852,6 +971,7 @@ const SubscriptionsPage = ({ environmentName }) => {
852
971
  if (subscriptionId) return subscriptions.find((s) => s.id === subscriptionId) ?? subscriptions[0];
853
972
  return subscriptions[0];
854
973
  }, [subscriptions, subscriptionId]);
974
+ const activePhase = activeSubscription.phases.find((p) => new Date(p.activeFrom) <= /* @__PURE__ */ new Date() && new Date(p.activeTo) >= /* @__PURE__ */ new Date());
855
975
  return /* @__PURE__ */ jsx("div", {
856
976
  className: "w-full py-12",
857
977
  children: /* @__PURE__ */ jsxs("div", {
@@ -870,7 +990,8 @@ const SubscriptionsPage = ({ environmentName }) => {
870
990
  }),
871
991
  activeSubscription && /* @__PURE__ */ jsx(Usage, {
872
992
  subscriptionId: activeSubscription?.id,
873
- environmentName
993
+ environmentName,
994
+ currentItems: activePhase?.items
874
995
  }),
875
996
  activeSubscription?.consumer?.apiKeys && /* @__PURE__ */ jsx(ApiKeysList, {
876
997
  deploymentName: environmentName,
@@ -905,62 +1026,60 @@ const enableMonetization = (config, options) => {
905
1026
  } }
906
1027
  };
907
1028
  };
908
- const zuploMonetizationPlugin = (options) => {
909
- return {
910
- getIdentities: async (context) => {
911
- return (await queryClient.fetchQuery({
912
- queryKey: [`/v3/zudoku-metering/${options.environmentName}/subscriptions`],
913
- meta: { context }
914
- })).items.flatMap((item) => item.consumer.apiKeys.map((apiKey) => ({
915
- label: item.name,
916
- id: apiKey.id,
917
- authorizeRequest: async (request) => {
918
- return new Request(request, { headers: { Authorization: `Bearer ${apiKey.key}` } });
919
- },
920
- authorizationFields: { headers: ["Authorization"] }
921
- })));
922
- },
923
- getProfileMenuItems: () => [{
924
- label: "My Subscriptions",
925
- path: "/subscriptions",
926
- icon: StarsIcon
927
- }],
928
- getRoutes: () => [{
929
- Component: ZuploMonetizationWrapper_default,
930
- handle: { layout: "none" },
931
- children: [{
932
- path: "/checkout",
933
- element: /* @__PURE__ */ jsx(CheckoutPage_default, { environmentName: options.environmentName })
934
- }, {
935
- path: "/checkout-confirm",
936
- element: /* @__PURE__ */ jsx(CheckoutConfimPage_default, { environmentName: options.environmentName })
937
- }]
1029
+ const zuploMonetizationPlugin = createPlugin((options) => ({
1030
+ getIdentities: async (context) => {
1031
+ return (await queryClient.fetchQuery({
1032
+ queryKey: [`/v3/zudoku-metering/${options.environmentName}/subscriptions`],
1033
+ meta: { context }
1034
+ })).items.flatMap((item) => item.consumer.apiKeys.map((apiKey) => ({
1035
+ label: item.name,
1036
+ id: apiKey.id,
1037
+ authorizeRequest: async (request) => {
1038
+ return new Request(request, { headers: { Authorization: `Bearer ${apiKey.key}` } });
1039
+ },
1040
+ authorizationFields: { headers: ["Authorization"] }
1041
+ })));
1042
+ },
1043
+ getProfileMenuItems: () => [{
1044
+ label: "My Subscriptions",
1045
+ path: "/subscriptions",
1046
+ icon: StarsIcon
1047
+ }],
1048
+ getRoutes: () => [{
1049
+ Component: ZuploMonetizationWrapper_default,
1050
+ handle: { layout: "none" },
1051
+ children: [{
1052
+ path: "/checkout",
1053
+ element: /* @__PURE__ */ jsx(CheckoutPage_default, { environmentName: options.environmentName })
938
1054
  }, {
939
- Component: ZuploMonetizationWrapper_default,
940
- children: [
941
- {
942
- path: "/pricing",
943
- element: /* @__PURE__ */ jsx(PricingPage_default, { environmentName: options.environmentName })
944
- },
945
- {
946
- path: "/checkout-failed",
947
- element: /* @__PURE__ */ jsx(CheckoutFailedPage_default, {})
948
- },
949
- {
950
- path: "/subscriptions/:subscriptionId?",
951
- element: /* @__PURE__ */ jsx(SubscriptionsPage_default, { environmentName: options.environmentName })
952
- }
953
- ]
954
- }],
955
- getProtectedRoutes: () => {
956
- return [
957
- "/checkout",
958
- "/checkout-success",
959
- "/checkout-failed"
960
- ];
961
- }
962
- };
963
- };
1055
+ path: "/checkout-confirm",
1056
+ element: /* @__PURE__ */ jsx(CheckoutConfimPage_default, { environmentName: options.environmentName })
1057
+ }]
1058
+ }, {
1059
+ Component: ZuploMonetizationWrapper_default,
1060
+ children: [
1061
+ {
1062
+ path: "/pricing",
1063
+ element: /* @__PURE__ */ jsx(PricingPage_default, { environmentName: options.environmentName })
1064
+ },
1065
+ {
1066
+ path: "/checkout-failed",
1067
+ element: /* @__PURE__ */ jsx(CheckoutFailedPage_default, {})
1068
+ },
1069
+ {
1070
+ path: "/subscriptions/:subscriptionId?",
1071
+ element: /* @__PURE__ */ jsx(SubscriptionsPage_default, { environmentName: options.environmentName })
1072
+ }
1073
+ ]
1074
+ }],
1075
+ getProtectedRoutes: () => {
1076
+ return [
1077
+ "/checkout",
1078
+ "/checkout-success",
1079
+ "/checkout-failed"
1080
+ ];
1081
+ }
1082
+ }), import.meta.url);
964
1083
 
965
1084
  //#endregion
966
1085
  export { enableMonetization, zuploMonetizationPlugin };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zuplo/zudoku-plugin-monetization",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "main": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.mts",
@@ -27,7 +27,7 @@
27
27
  "peerDependencies": {
28
28
  "react": ">=19.2.0",
29
29
  "react-dom": ">=19.2.0",
30
- "zudoku": ">=0.67.0"
30
+ "zudoku": "*"
31
31
  },
32
32
  "scripts": {
33
33
  "build": "tsdown",