@zuplo/zudoku-plugin-monetization 0.0.4 → 0.0.6
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 +39 -2
- package/dist/index.mjs +236 -117
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,47 @@
|
|
|
1
|
-
import
|
|
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) =>
|
|
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 {
|
|
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 {
|
|
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: "
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
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__ */
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
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
|
|
787
|
-
children: /* @__PURE__ */
|
|
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
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
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 ?? "Limit",
|
|
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__ */
|
|
818
|
-
className: "flex flex-col gap-2",
|
|
819
|
-
children:
|
|
820
|
-
className: "text-
|
|
821
|
-
children:
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
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-
|
|
828
|
-
children: [
|
|
930
|
+
className: "text-foreground font-medium",
|
|
931
|
+
children: [meter.balance.toLocaleString(), " limit"]
|
|
829
932
|
})]
|
|
830
933
|
}),
|
|
831
934
|
/* @__PURE__ */ jsx(Progress, {
|
|
832
|
-
value:
|
|
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-
|
|
837
|
-
children: [
|
|
939
|
+
className: "text-xs text-muted-foreground",
|
|
940
|
+
children: [(meter.balance - meter.usage).toLocaleString(), " remaining this month"]
|
|
838
941
|
})
|
|
839
942
|
]
|
|
840
|
-
})]
|
|
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
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
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
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
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.
|
|
3
|
+
"version": "0.0.6",
|
|
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": "
|
|
30
|
+
"zudoku": "*"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "tsdown",
|