@zuplo/zudoku-plugin-monetization 0.0.31 → 0.0.32
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 +2 -3
- package/dist/index.mjs +250 -53
- package/package.json +6 -6
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import "
|
|
2
|
-
import * as zudoku from "zudoku";
|
|
1
|
+
import * as _$zudoku from "zudoku";
|
|
3
2
|
|
|
4
3
|
//#region src/MonetizationContext.d.ts
|
|
5
4
|
interface MonetizationConfig {
|
|
@@ -12,6 +11,6 @@ interface MonetizationConfig {
|
|
|
12
11
|
}
|
|
13
12
|
//#endregion
|
|
14
13
|
//#region src/ZuploMonetizationPlugin.d.ts
|
|
15
|
-
declare const zuploMonetizationPlugin: (options?: MonetizationConfig | undefined) => zudoku.ZudokuPlugin;
|
|
14
|
+
declare const zuploMonetizationPlugin: (options?: MonetizationConfig | undefined) => _$zudoku.ZudokuPlugin;
|
|
16
15
|
//#endregion
|
|
17
16
|
export { zuploMonetizationPlugin };
|
package/dist/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { useAuth, useZudoku } from "zudoku/hooks";
|
|
|
6
6
|
import { QueryClient, QueryClientProvider, useMutation, useQuery, useQueryClient, useSuspenseQuery } from "zudoku/react-query";
|
|
7
7
|
import { Link as Link$1, Outlet, useLocation, useNavigate, useSearchParams } from "zudoku/router";
|
|
8
8
|
import { Alert, AlertAction, AlertDescription, AlertTitle } from "zudoku/ui/Alert";
|
|
9
|
-
import { Card, CardContent, CardHeader, CardTitle } from "zudoku/ui/Card";
|
|
9
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "zudoku/ui/Card";
|
|
10
10
|
import { Separator } from "zudoku/ui/Separator";
|
|
11
11
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
12
12
|
import { parse } from "tinyduration";
|
|
@@ -23,7 +23,6 @@ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
|
|
|
23
23
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "zudoku/ui/Dialog";
|
|
24
24
|
import { Input } from "zudoku/ui/Input";
|
|
25
25
|
import { Progress } from "zudoku/ui/Progress";
|
|
26
|
-
|
|
27
26
|
//#region src/components/FeatureItem.tsx
|
|
28
27
|
const FeatureItem = ({ feature, className }) => {
|
|
29
28
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -41,7 +40,6 @@ const FeatureItem = ({ feature, className }) => {
|
|
|
41
40
|
})]
|
|
42
41
|
});
|
|
43
42
|
};
|
|
44
|
-
|
|
45
43
|
//#endregion
|
|
46
44
|
//#region src/components/QuotaItem.tsx
|
|
47
45
|
const QuotaItem = ({ quota, className }) => {
|
|
@@ -70,7 +68,6 @@ const QuotaItem = ({ quota, className }) => {
|
|
|
70
68
|
})]
|
|
71
69
|
});
|
|
72
70
|
};
|
|
73
|
-
|
|
74
71
|
//#endregion
|
|
75
72
|
//#region src/hooks/useDeploymentName.ts
|
|
76
73
|
const useDeploymentName = () => {
|
|
@@ -78,7 +75,6 @@ const useDeploymentName = () => {
|
|
|
78
75
|
if (!deploymentName) throw new Error("ZUPLO_PUBLIC_DEPLOYMENT_NAME is not set");
|
|
79
76
|
return deploymentName;
|
|
80
77
|
};
|
|
81
|
-
|
|
82
78
|
//#endregion
|
|
83
79
|
//#region src/hooks/usePurchaseDetails.ts
|
|
84
80
|
const usePurchaseDetails = (planId) => {
|
|
@@ -88,12 +84,10 @@ const usePurchaseDetails = (planId) => {
|
|
|
88
84
|
meta: { context: zudoku }
|
|
89
85
|
});
|
|
90
86
|
};
|
|
91
|
-
|
|
92
87
|
//#endregion
|
|
93
88
|
//#region src/MonetizationContext.tsx
|
|
94
89
|
const MonetizationContext = createContext({});
|
|
95
90
|
const useMonetizationConfig = () => use(MonetizationContext);
|
|
96
|
-
|
|
97
91
|
//#endregion
|
|
98
92
|
//#region src/utils/formatDuration.ts
|
|
99
93
|
const formatDuration = (iso) => {
|
|
@@ -146,7 +140,6 @@ const formatDurationAdjective = (iso) => {
|
|
|
146
140
|
return "billing period";
|
|
147
141
|
}
|
|
148
142
|
};
|
|
149
|
-
|
|
150
143
|
//#endregion
|
|
151
144
|
//#region src/utils/formatPrice.ts
|
|
152
145
|
const formatPrice = (amount, currency) => new Intl.NumberFormat("en-US", {
|
|
@@ -171,7 +164,6 @@ const formatMinorCurrencyAmount = (amountInMinorUnits, currency) => {
|
|
|
171
164
|
maximumFractionDigits: fractionDigits
|
|
172
165
|
}).format(amountInMinorUnits / divisor);
|
|
173
166
|
};
|
|
174
|
-
|
|
175
167
|
//#endregion
|
|
176
168
|
//#region src/utils/categorizeRateCards.ts
|
|
177
169
|
const categorizeRateCards = (rateCards, options) => {
|
|
@@ -221,7 +213,6 @@ const categorizeRateCards = (rateCards, options) => {
|
|
|
221
213
|
features
|
|
222
214
|
};
|
|
223
215
|
};
|
|
224
|
-
|
|
225
216
|
//#endregion
|
|
226
217
|
//#region src/utils/formatBillingCycle.ts
|
|
227
218
|
const formatBillingCycle = (duration) => {
|
|
@@ -231,7 +222,6 @@ const formatBillingCycle = (duration) => {
|
|
|
231
222
|
if (duration === "day") return "daily";
|
|
232
223
|
return `every ${duration}`;
|
|
233
224
|
};
|
|
234
|
-
|
|
235
225
|
//#endregion
|
|
236
226
|
//#region src/utils/getPriceFromPlan.ts
|
|
237
227
|
const getPriceFromPlan = (plan) => {
|
|
@@ -240,7 +230,6 @@ const getPriceFromPlan = (plan) => {
|
|
|
240
230
|
yearly: plan.yearlyPrice != null ? parseFloat(plan.yearlyPrice) : 0
|
|
241
231
|
};
|
|
242
232
|
};
|
|
243
|
-
|
|
244
233
|
//#endregion
|
|
245
234
|
//#region src/utils/purchaseDetails.ts
|
|
246
235
|
const getPlanFromPurchaseDetails = (response) => {
|
|
@@ -258,7 +247,6 @@ const getTaxLabelFromPurchaseDetails = (response) => {
|
|
|
258
247
|
const isTaxInclusiveFromPurchaseDetails = (response) => {
|
|
259
248
|
return response.tax?.taxInclusive === true;
|
|
260
249
|
};
|
|
261
|
-
|
|
262
250
|
//#endregion
|
|
263
251
|
//#region src/ZuploMonetizationWrapper.tsx
|
|
264
252
|
const DEFAULT_GATEWAY_URL = "https://api.zuploedge.com";
|
|
@@ -330,7 +318,6 @@ const ZuploMonetizationWrapper = ({ options = {} }) => /* @__PURE__ */ jsx(Query
|
|
|
330
318
|
children: /* @__PURE__ */ jsx(ClientOnly, { children: /* @__PURE__ */ jsx(Outlet, {}) })
|
|
331
319
|
})
|
|
332
320
|
});
|
|
333
|
-
|
|
334
321
|
//#endregion
|
|
335
322
|
//#region src/pages/CheckoutConfirmPage.tsx
|
|
336
323
|
const CheckoutConfirmPage = () => {
|
|
@@ -494,7 +481,6 @@ const CheckoutConfirmPage = () => {
|
|
|
494
481
|
})
|
|
495
482
|
});
|
|
496
483
|
};
|
|
497
|
-
|
|
498
484
|
//#endregion
|
|
499
485
|
//#region src/components/RedirectPage.tsx
|
|
500
486
|
const RedirectPage = ({ icon: Icon, title, description, url, children }) => {
|
|
@@ -543,7 +529,6 @@ const RedirectPage = ({ icon: Icon, title, description, url, children }) => {
|
|
|
543
529
|
})
|
|
544
530
|
});
|
|
545
531
|
};
|
|
546
|
-
|
|
547
532
|
//#endregion
|
|
548
533
|
//#region src/hooks/useUrlUtils.ts
|
|
549
534
|
const useUrlUtils = () => {
|
|
@@ -553,7 +538,6 @@ const useUrlUtils = () => {
|
|
|
553
538
|
return joinUrl(window.location.origin, basePath, path, searchParams ? `?${new URLSearchParams(searchParams)}` : void 0);
|
|
554
539
|
} };
|
|
555
540
|
};
|
|
556
|
-
|
|
557
541
|
//#endregion
|
|
558
542
|
//#region src/pages/CheckoutPage.tsx
|
|
559
543
|
const CheckoutPage = () => {
|
|
@@ -604,7 +588,6 @@ const CheckoutPage = () => {
|
|
|
604
588
|
})
|
|
605
589
|
});
|
|
606
590
|
};
|
|
607
|
-
|
|
608
591
|
//#endregion
|
|
609
592
|
//#region src/pages/ManagePaymentPage.tsx
|
|
610
593
|
const ManagePaymentPage = () => {
|
|
@@ -647,7 +630,6 @@ const ManagePaymentPage = () => {
|
|
|
647
630
|
})
|
|
648
631
|
});
|
|
649
632
|
};
|
|
650
|
-
|
|
651
633
|
//#endregion
|
|
652
634
|
//#region src/hooks/usePlans.ts
|
|
653
635
|
const usePlans = () => {
|
|
@@ -658,7 +640,6 @@ const usePlans = () => {
|
|
|
658
640
|
meta: { context: auth.isAuthenticated ? zudoku : void 0 }
|
|
659
641
|
});
|
|
660
642
|
};
|
|
661
|
-
|
|
662
643
|
//#endregion
|
|
663
644
|
//#region src/pages/pricing/PricingCard.tsx
|
|
664
645
|
const PhaseSection = ({ phase, currency, showName, billingCadence }) => {
|
|
@@ -765,7 +746,6 @@ const PricingCard = ({ plan, isPopular = false, isSubscribed = false }) => {
|
|
|
765
746
|
]
|
|
766
747
|
});
|
|
767
748
|
};
|
|
768
|
-
|
|
769
749
|
//#endregion
|
|
770
750
|
//#region src/pages/PricingPage.tsx
|
|
771
751
|
const PricingPage = () => {
|
|
@@ -816,7 +796,6 @@ const PricingPage = () => {
|
|
|
816
796
|
]
|
|
817
797
|
});
|
|
818
798
|
};
|
|
819
|
-
|
|
820
799
|
//#endregion
|
|
821
800
|
//#region src/pages/PricingPageSkeleton.tsx
|
|
822
801
|
const PricingPageSkeleton = () => /* @__PURE__ */ jsxs("div", {
|
|
@@ -847,7 +826,6 @@ const PricingPageSkeleton = () => /* @__PURE__ */ jsxs("div", {
|
|
|
847
826
|
}, i))
|
|
848
827
|
})]
|
|
849
828
|
});
|
|
850
|
-
|
|
851
829
|
//#endregion
|
|
852
830
|
//#region src/pages/SubscriptionChangeConfirmPage.tsx
|
|
853
831
|
const SubscriptionChangeConfirmPage = () => {
|
|
@@ -1015,7 +993,6 @@ const SubscriptionChangeConfirmPage = () => {
|
|
|
1015
993
|
})
|
|
1016
994
|
});
|
|
1017
995
|
};
|
|
1018
|
-
|
|
1019
996
|
//#endregion
|
|
1020
997
|
//#region src/hooks/useSubscriptions.ts
|
|
1021
998
|
const useSubscriptions = (environmentName) => {
|
|
@@ -1033,7 +1010,6 @@ const useSubscriptions = (environmentName) => {
|
|
|
1033
1010
|
})
|
|
1034
1011
|
});
|
|
1035
1012
|
};
|
|
1036
|
-
|
|
1037
1013
|
//#endregion
|
|
1038
1014
|
//#region src/pages/subscriptions/ConfirmDeleteKeyAlert.tsx
|
|
1039
1015
|
const ConfirmDeleteKeyAlert = ({ children, onDelete }) => {
|
|
@@ -1045,10 +1021,9 @@ const ConfirmDeleteKeyAlert = ({ children, onDelete }) => {
|
|
|
1045
1021
|
children: "Continue"
|
|
1046
1022
|
})] })] })] });
|
|
1047
1023
|
};
|
|
1048
|
-
|
|
1049
1024
|
//#endregion
|
|
1050
1025
|
//#region src/pages/subscriptions/ApiKey.tsx
|
|
1051
|
-
const formatDate$
|
|
1026
|
+
const formatDate$2 = (dateString) => {
|
|
1052
1027
|
if (!dateString) return "";
|
|
1053
1028
|
return new Date(dateString).toLocaleDateString("en-US", {
|
|
1054
1029
|
month: "short",
|
|
@@ -1059,8 +1034,7 @@ const formatDate$1 = (dateString) => {
|
|
|
1059
1034
|
const getTimeAgo = (dateString) => {
|
|
1060
1035
|
if (!dateString) return "Never";
|
|
1061
1036
|
const date = new Date(dateString);
|
|
1062
|
-
const
|
|
1063
|
-
const diffInMinutes = Math.floor((now.getTime() - date.getTime()) / (1e3 * 60));
|
|
1037
|
+
const diffInMinutes = Math.floor(((/* @__PURE__ */ new Date()).getTime() - date.getTime()) / (1e3 * 60));
|
|
1064
1038
|
if (diffInMinutes < 1) return "Just now";
|
|
1065
1039
|
if (diffInMinutes < 60) return `${diffInMinutes} minutes ago`;
|
|
1066
1040
|
const diffInHours = Math.floor(diffInMinutes / 60);
|
|
@@ -1068,7 +1042,7 @@ const getTimeAgo = (dateString) => {
|
|
|
1068
1042
|
const diffInDays = Math.floor(diffInHours / 24);
|
|
1069
1043
|
if (diffInDays === 1) return "1 day ago";
|
|
1070
1044
|
if (diffInDays < 30) return `${diffInDays} days ago`;
|
|
1071
|
-
return formatDate$
|
|
1045
|
+
return formatDate$2(dateString);
|
|
1072
1046
|
};
|
|
1073
1047
|
const ApiKey = ({ apiKey, createdAt, lastUsed, expiresOn, isActive = true, label, onDelete }) => {
|
|
1074
1048
|
const isExpiring = expiresOn && new Date(expiresOn) < new Date(Date.now() + 720 * 60 * 60 * 1e3);
|
|
@@ -1125,7 +1099,7 @@ const ApiKey = ({ apiKey, createdAt, lastUsed, expiresOn, isActive = true, label
|
|
|
1125
1099
|
children: [
|
|
1126
1100
|
/* @__PURE__ */ jsxs("div", {
|
|
1127
1101
|
className: "flex items-center gap-1.5",
|
|
1128
|
-
children: [/* @__PURE__ */ jsx(ClockIcon, { className: "size-3" }), /* @__PURE__ */ jsxs("span", { children: ["Created ", formatDate$
|
|
1102
|
+
children: [/* @__PURE__ */ jsx(ClockIcon, { className: "size-3" }), /* @__PURE__ */ jsxs("span", { children: ["Created ", formatDate$2(createdAt)] })]
|
|
1129
1103
|
}),
|
|
1130
1104
|
/* @__PURE__ */ jsx("span", {
|
|
1131
1105
|
className: "text-muted-foreground/40",
|
|
@@ -1140,7 +1114,7 @@ const ApiKey = ({ apiKey, createdAt, lastUsed, expiresOn, isActive = true, label
|
|
|
1140
1114
|
children: [
|
|
1141
1115
|
isExpired ? "Expired" : "Expires",
|
|
1142
1116
|
" on ",
|
|
1143
|
-
formatDate$
|
|
1117
|
+
formatDate$2(expiresOn)
|
|
1144
1118
|
]
|
|
1145
1119
|
})] })
|
|
1146
1120
|
]
|
|
@@ -1148,7 +1122,6 @@ const ApiKey = ({ apiKey, createdAt, lastUsed, expiresOn, isActive = true, label
|
|
|
1148
1122
|
})]
|
|
1149
1123
|
});
|
|
1150
1124
|
};
|
|
1151
|
-
|
|
1152
1125
|
//#endregion
|
|
1153
1126
|
//#region src/pages/subscriptions/ApiKeyInfo.tsx
|
|
1154
1127
|
const ApiKeyInfo = () => /* @__PURE__ */ jsxs(DismissibleAlert, {
|
|
@@ -1168,7 +1141,6 @@ const ApiKeyInfo = () => /* @__PURE__ */ jsxs(DismissibleAlert, {
|
|
|
1168
1141
|
/* @__PURE__ */ jsx(DismissibleAlertAction, {})
|
|
1169
1142
|
]
|
|
1170
1143
|
});
|
|
1171
|
-
|
|
1172
1144
|
//#endregion
|
|
1173
1145
|
//#region src/pages/subscriptions/ConfirmRollKeyAlert.tsx
|
|
1174
1146
|
const ConfirmRollKeyAlert = (props) => /* @__PURE__ */ jsxs(AlertDialog, { children: [/* @__PURE__ */ jsx(AlertDialogTrigger, {
|
|
@@ -1178,7 +1150,6 @@ const ConfirmRollKeyAlert = (props) => /* @__PURE__ */ jsxs(AlertDialog, { child
|
|
|
1178
1150
|
onClick: props.onRollKey,
|
|
1179
1151
|
children: "Continue"
|
|
1180
1152
|
})] })] })] });
|
|
1181
|
-
|
|
1182
1153
|
//#endregion
|
|
1183
1154
|
//#region src/pages/subscriptions/ApiKeysList.tsx
|
|
1184
1155
|
const PendingFirstPaymentAlert = ({ children }) => /* @__PURE__ */ jsxs("div", {
|
|
@@ -1258,7 +1229,7 @@ const ApiKeysList = ({ isPendingFirstPayment, apiKeys, deploymentName, consumerI
|
|
|
1258
1229
|
/* @__PURE__ */ jsx(AlertTitle, { children: "API key was deleted" }),
|
|
1259
1230
|
/* @__PURE__ */ jsx(AlertDescription, { children: (() => {
|
|
1260
1231
|
const deletedKey = apiKeys.find((k) => k.id === deleteKeyMutation.variables?.keyId);
|
|
1261
|
-
return deletedKey ? `API key created ${formatDate$
|
|
1232
|
+
return deletedKey ? `API key created ${formatDate$2(deletedKey.createdOn)} has been removed.` : "The API key has been deleted.";
|
|
1262
1233
|
})() }),
|
|
1263
1234
|
/* @__PURE__ */ jsx(DismissibleAlertAction, {})
|
|
1264
1235
|
]
|
|
@@ -1330,7 +1301,6 @@ const ApiKeysList = ({ isPendingFirstPayment, apiKeys, deploymentName, consumerI
|
|
|
1330
1301
|
]
|
|
1331
1302
|
});
|
|
1332
1303
|
};
|
|
1333
|
-
|
|
1334
1304
|
//#endregion
|
|
1335
1305
|
//#region src/pages/subscriptions/CancelSubscriptionDialog.tsx
|
|
1336
1306
|
const CancelSubscriptionDialog = ({ open, onOpenChange, planName, subscriptionId, billingPeriodEnd }) => {
|
|
@@ -1370,7 +1340,7 @@ const CancelSubscriptionDialog = ({ open, onOpenChange, planName, subscriptionId
|
|
|
1370
1340
|
/* @__PURE__ */ jsx(AlertTitle, { children: "Your plan will be canceled at the end of your billing cycle." }),
|
|
1371
1341
|
/* @__PURE__ */ jsxs(AlertDescription, { children: [
|
|
1372
1342
|
"You'll retain access until ",
|
|
1373
|
-
formatDate$
|
|
1343
|
+
formatDate$2(billingPeriodEnd),
|
|
1374
1344
|
". After your billing period ends, this plan will not renew and you would need to subscribe again to continue."
|
|
1375
1345
|
] })
|
|
1376
1346
|
]
|
|
@@ -1383,7 +1353,7 @@ const CancelSubscriptionDialog = ({ open, onOpenChange, planName, subscriptionId
|
|
|
1383
1353
|
/* @__PURE__ */ jsxs(AlertDescription, { children: [
|
|
1384
1354
|
"If you change your mind you have until",
|
|
1385
1355
|
" ",
|
|
1386
|
-
formatDate$
|
|
1356
|
+
formatDate$2(billingPeriodEnd),
|
|
1387
1357
|
" to remove this cancellation from Manage subscription."
|
|
1388
1358
|
] })
|
|
1389
1359
|
]
|
|
@@ -1437,7 +1407,6 @@ const CancelSubscriptionDialog = ({ open, onOpenChange, planName, subscriptionId
|
|
|
1437
1407
|
})
|
|
1438
1408
|
});
|
|
1439
1409
|
};
|
|
1440
|
-
|
|
1441
1410
|
//#endregion
|
|
1442
1411
|
//#region src/pages/subscriptions/RestoreSubscriptionDialog.tsx
|
|
1443
1412
|
const RestoreSubscriptionDialog = ({ open, onOpenChange, planName, subscriptionId, billingPeriodEnd }) => {
|
|
@@ -1487,7 +1456,7 @@ const RestoreSubscriptionDialog = ({ open, onOpenChange, planName, subscriptionI
|
|
|
1487
1456
|
className: "space-y-2",
|
|
1488
1457
|
children: [/* @__PURE__ */ jsxs("p", { children: [
|
|
1489
1458
|
"Your access stays in place until ",
|
|
1490
|
-
formatDate$
|
|
1459
|
+
formatDate$2(billingPeriodEnd),
|
|
1491
1460
|
" ",
|
|
1492
1461
|
"either way."
|
|
1493
1462
|
] }), /* @__PURE__ */ jsx("p", { children: "Confirming will remove the pending cancellation. Your subscription will remain active and continue to renew on your normal billing schedule, and charges will apply as usual." })]
|
|
@@ -1519,7 +1488,6 @@ const RestoreSubscriptionDialog = ({ open, onOpenChange, planName, subscriptionI
|
|
|
1519
1488
|
})
|
|
1520
1489
|
});
|
|
1521
1490
|
};
|
|
1522
|
-
|
|
1523
1491
|
//#endregion
|
|
1524
1492
|
//#region src/pages/subscriptions/SwitchPlanModal.tsx
|
|
1525
1493
|
const getAllKeysAcrossPhases = (plan, units) => {
|
|
@@ -1669,6 +1637,7 @@ const ChangeIndicator = ({ change }) => {
|
|
|
1669
1637
|
if (change === "decrease" || change === "removed" || change === "downgraded") return /* @__PURE__ */ jsx(ArrowDownIcon, { className: "w-4 h-4 text-amber-600 shrink-0" });
|
|
1670
1638
|
return /* @__PURE__ */ jsx(CheckIcon, { className: "w-4 h-4 text-green-600 shrink-0" });
|
|
1671
1639
|
};
|
|
1640
|
+
const isPrivatePlan = (plan) => plan.metadata?.zuplo_private_plan === "true";
|
|
1672
1641
|
const modeLabelMap = {
|
|
1673
1642
|
upgrade: "Upgrade",
|
|
1674
1643
|
downgrade: "Downgrade",
|
|
@@ -1859,12 +1828,29 @@ const SwitchPlanModal = ({ subscription, children }) => {
|
|
|
1859
1828
|
});
|
|
1860
1829
|
const currentPlan = plansData?.items.find((p) => p.key === subscription.plan.key);
|
|
1861
1830
|
const { upgrades, downgrades, privatePlans } = useMemo(() => {
|
|
1862
|
-
if (!plansData?.items
|
|
1831
|
+
if (!plansData?.items) return {
|
|
1863
1832
|
upgrades: [],
|
|
1864
1833
|
downgrades: [],
|
|
1865
1834
|
privatePlans: []
|
|
1866
1835
|
};
|
|
1867
|
-
|
|
1836
|
+
if (!currentPlan) {
|
|
1837
|
+
const currentIndex = -1;
|
|
1838
|
+
return {
|
|
1839
|
+
upgrades: plansData.items.map((plan, targetIndex) => comparePlans(void 0, plan, currentIndex, targetIndex, pricing?.units)).filter((c) => !isPrivatePlan(c.plan)),
|
|
1840
|
+
downgrades: [],
|
|
1841
|
+
privatePlans: []
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
if (isPrivatePlan(currentPlan)) {
|
|
1845
|
+
const currentIndex = plansData.items.findIndex((p) => p.id === currentPlan.id);
|
|
1846
|
+
return {
|
|
1847
|
+
upgrades: plansData.items.filter((p) => p.id !== currentPlan.id).map((plan) => {
|
|
1848
|
+
return comparePlans(currentPlan, plan, currentIndex, plansData.items.indexOf(plan), pricing?.units);
|
|
1849
|
+
}).filter((c) => !isPrivatePlan(c.plan)),
|
|
1850
|
+
downgrades: [],
|
|
1851
|
+
privatePlans: []
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1868
1854
|
const currentIndex = plansData.items.findIndex((p) => p.id === currentPlan.id);
|
|
1869
1855
|
const allComparisons = plansData.items.filter((p) => p.id !== currentPlan.id).map((plan) => {
|
|
1870
1856
|
return comparePlans(currentPlan, plan, currentIndex, plansData.items.indexOf(plan), pricing?.units);
|
|
@@ -1914,6 +1900,13 @@ const SwitchPlanModal = ({ subscription, children }) => {
|
|
|
1914
1900
|
children: currentPlan.name
|
|
1915
1901
|
})] })
|
|
1916
1902
|
}),
|
|
1903
|
+
!currentPlan && /* @__PURE__ */ jsx(Item, {
|
|
1904
|
+
variant: "outline",
|
|
1905
|
+
children: /* @__PURE__ */ jsxs(ItemContent, { children: [/* @__PURE__ */ jsx(ItemTitle, { children: "Current Plan" }), /* @__PURE__ */ jsx(ItemDescription, {
|
|
1906
|
+
className: "text-lg font-bold",
|
|
1907
|
+
children: subscription.plan.name
|
|
1908
|
+
})] })
|
|
1909
|
+
}),
|
|
1917
1910
|
upgrades.length > 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
|
|
1918
1911
|
className: "flex items-center justify-between mb-3",
|
|
1919
1912
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
@@ -1985,7 +1978,6 @@ const SwitchPlanModal = ({ subscription, children }) => {
|
|
|
1985
1978
|
}) })]
|
|
1986
1979
|
});
|
|
1987
1980
|
};
|
|
1988
|
-
|
|
1989
1981
|
//#endregion
|
|
1990
1982
|
//#region src/pages/subscriptions/ManageSubscription.tsx
|
|
1991
1983
|
const ManageSubscription = ({ subscription, planName }) => {
|
|
@@ -2077,7 +2069,217 @@ const ManageSubscription = ({ subscription, planName }) => {
|
|
|
2077
2069
|
})
|
|
2078
2070
|
] });
|
|
2079
2071
|
};
|
|
2080
|
-
|
|
2072
|
+
//#endregion
|
|
2073
|
+
//#region src/pages/subscriptions/SubscriptionPlanDetails.tsx
|
|
2074
|
+
const detailLabelClassName = "text-sm font-semibold tracking-wide mb-1";
|
|
2075
|
+
const sectionLabelClassName = "text-base font-semibold tracking-wide mb-3 mt-2";
|
|
2076
|
+
const formatDate$1 = (dateString) => {
|
|
2077
|
+
return new Date(dateString).toLocaleDateString("en-US", {
|
|
2078
|
+
month: "short",
|
|
2079
|
+
day: "numeric",
|
|
2080
|
+
year: "numeric"
|
|
2081
|
+
});
|
|
2082
|
+
};
|
|
2083
|
+
const formatDateRange = (from, to) => `${formatDate$1(from)} – ${formatDate$1(to)}`;
|
|
2084
|
+
const formatNumber = (value) => value.toLocaleString("en-US");
|
|
2085
|
+
const getOveragePriceFromItem = (item, currency, units) => {
|
|
2086
|
+
const tiers = item.price?.tiers;
|
|
2087
|
+
if (!tiers || tiers.length === 0) return void 0;
|
|
2088
|
+
const amount = tiers.find((t) => {
|
|
2089
|
+
const amount = t.unitPrice?.amount;
|
|
2090
|
+
if (!amount) return false;
|
|
2091
|
+
const parsed = parseFloat(amount);
|
|
2092
|
+
return Number.isFinite(parsed) && parsed > 0;
|
|
2093
|
+
})?.unitPrice?.amount;
|
|
2094
|
+
if (!amount) return void 0;
|
|
2095
|
+
const parsed = parseFloat(amount);
|
|
2096
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return void 0;
|
|
2097
|
+
const unitLabel = units?.[item.key] ?? units?.[item.featureKey] ?? "unit";
|
|
2098
|
+
return `${formatPrice(parsed, currency)}/${unitLabel}`;
|
|
2099
|
+
};
|
|
2100
|
+
const getEntitlementsFromItems = (items, currency, units, fallbackBillingCadence) => {
|
|
2101
|
+
const features = [];
|
|
2102
|
+
for (const item of items) {
|
|
2103
|
+
const entitlement = item.included?.entitlement;
|
|
2104
|
+
if (!entitlement) continue;
|
|
2105
|
+
if (entitlement.type === "metered" && entitlement.issueAfterReset != null) {
|
|
2106
|
+
const cadence = item.billingCadence ?? fallbackBillingCadence;
|
|
2107
|
+
features.push({
|
|
2108
|
+
entitlementType: "metered",
|
|
2109
|
+
key: item.featureKey ?? item.key,
|
|
2110
|
+
name: item.name ?? item.featureKey ?? item.key,
|
|
2111
|
+
limit: entitlement.issueAfterReset,
|
|
2112
|
+
period: cadence ? formatDuration(cadence) : "month",
|
|
2113
|
+
overagePrice: entitlement.isSoftLimit !== false ? getOveragePriceFromItem(item, currency, units) : void 0
|
|
2114
|
+
});
|
|
2115
|
+
continue;
|
|
2116
|
+
}
|
|
2117
|
+
if (entitlement.type === "boolean") {
|
|
2118
|
+
features.push({
|
|
2119
|
+
entitlementType: "boolean",
|
|
2120
|
+
key: item.featureKey ?? item.key,
|
|
2121
|
+
name: item.name ?? item.featureKey ?? item.key
|
|
2122
|
+
});
|
|
2123
|
+
continue;
|
|
2124
|
+
}
|
|
2125
|
+
if (entitlement.type === "static") {
|
|
2126
|
+
const base = {
|
|
2127
|
+
key: item.featureKey ?? item.key,
|
|
2128
|
+
name: item.name ?? item.featureKey ?? item.key
|
|
2129
|
+
};
|
|
2130
|
+
if (!entitlement.config) {
|
|
2131
|
+
features.push({
|
|
2132
|
+
entitlementType: "static",
|
|
2133
|
+
...base
|
|
2134
|
+
});
|
|
2135
|
+
continue;
|
|
2136
|
+
}
|
|
2137
|
+
try {
|
|
2138
|
+
const parsed = JSON.parse(entitlement.config);
|
|
2139
|
+
features.push({
|
|
2140
|
+
entitlementType: "static",
|
|
2141
|
+
...base,
|
|
2142
|
+
value: parsed?.value != null ? String(parsed.value) : void 0
|
|
2143
|
+
});
|
|
2144
|
+
} catch {
|
|
2145
|
+
features.push({
|
|
2146
|
+
entitlementType: "static",
|
|
2147
|
+
...base
|
|
2148
|
+
});
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
return { features };
|
|
2153
|
+
};
|
|
2154
|
+
const getPhaseRows = (opts) => {
|
|
2155
|
+
const { subscription, currency, units } = opts;
|
|
2156
|
+
const phases = [...subscription.phases].sort((a, b) => new Date(a.activeFrom).getTime() - new Date(b.activeFrom).getTime());
|
|
2157
|
+
const featureRows = [];
|
|
2158
|
+
for (const phase of phases) {
|
|
2159
|
+
const { features } = getEntitlementsFromItems(phase.items ?? [], currency, units, subscription.billingCadence);
|
|
2160
|
+
for (const f of features) featureRows.push({
|
|
2161
|
+
key: f.key,
|
|
2162
|
+
name: f.name,
|
|
2163
|
+
entitlementType: f.entitlementType,
|
|
2164
|
+
limit: f.entitlementType === "metered" ? f.limit : void 0,
|
|
2165
|
+
period: f.entitlementType === "metered" ? f.period : void 0,
|
|
2166
|
+
overagePrice: f.entitlementType === "metered" ? f.overagePrice : void 0,
|
|
2167
|
+
value: f.entitlementType === "static" ? f.value : void 0,
|
|
2168
|
+
phaseId: phase.id,
|
|
2169
|
+
activeFrom: phase.activeFrom,
|
|
2170
|
+
activeTo: phase.activeTo
|
|
2171
|
+
});
|
|
2172
|
+
}
|
|
2173
|
+
return { featureRows };
|
|
2174
|
+
};
|
|
2175
|
+
const formatActiveRange = (activeFrom, activeTo) => {
|
|
2176
|
+
if (!activeTo) return `Starts ${formatDate$1(activeFrom)}`;
|
|
2177
|
+
return `${formatDate$1(activeFrom)} – ${formatDate$1(activeTo)}`;
|
|
2178
|
+
};
|
|
2179
|
+
const SubscriptionPlanDetails = ({ subscription }) => {
|
|
2180
|
+
const { pricing } = useMonetizationConfig();
|
|
2181
|
+
const plan = subscription.plan;
|
|
2182
|
+
const currency = subscription.currency ?? plan.currency;
|
|
2183
|
+
const priceInfo = getPriceFromPlan(plan);
|
|
2184
|
+
const primaryPrice = priceInfo.monthly === 0 && priceInfo.yearly === 0 ? /* @__PURE__ */ jsx("span", {
|
|
2185
|
+
className: "text-primary font-medium",
|
|
2186
|
+
children: "Free"
|
|
2187
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
2188
|
+
className: "text-primary font-medium text-lg",
|
|
2189
|
+
children: formatPrice(priceInfo.monthly, currency)
|
|
2190
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
2191
|
+
className: "text-muted-foreground",
|
|
2192
|
+
children: [" / ", formatDuration(plan.billingCadence)]
|
|
2193
|
+
})] });
|
|
2194
|
+
const { featureRows } = getPhaseRows({
|
|
2195
|
+
subscription,
|
|
2196
|
+
currency,
|
|
2197
|
+
units: pricing?.units
|
|
2198
|
+
});
|
|
2199
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2200
|
+
className: "space-y-4",
|
|
2201
|
+
children: [/* @__PURE__ */ jsx(Heading, {
|
|
2202
|
+
level: 3,
|
|
2203
|
+
children: "Subscription Details"
|
|
2204
|
+
}), /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsxs(CardHeader, { children: [/* @__PURE__ */ jsx(CardTitle, {
|
|
2205
|
+
className: "text-lg font-semibold leading-tight",
|
|
2206
|
+
children: plan.name
|
|
2207
|
+
}), plan.description ? /* @__PURE__ */ jsx(CardDescription, { children: plan.description }) : null] }), /* @__PURE__ */ jsxs(CardContent, {
|
|
2208
|
+
className: "space-y-6",
|
|
2209
|
+
children: [/* @__PURE__ */ jsxs("dl", {
|
|
2210
|
+
className: "grid gap-4 sm:grid-cols-2 text-sm",
|
|
2211
|
+
children: [
|
|
2212
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", {
|
|
2213
|
+
className: detailLabelClassName,
|
|
2214
|
+
children: "Subscription ID"
|
|
2215
|
+
}), /* @__PURE__ */ jsx("dd", {
|
|
2216
|
+
className: "text-foreground font-mono text-xs break-all",
|
|
2217
|
+
children: subscription.id
|
|
2218
|
+
})] }),
|
|
2219
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", {
|
|
2220
|
+
className: detailLabelClassName,
|
|
2221
|
+
children: "Active since"
|
|
2222
|
+
}), /* @__PURE__ */ jsx("dd", {
|
|
2223
|
+
className: "text-foreground",
|
|
2224
|
+
children: formatDate$1(subscription.activeFrom)
|
|
2225
|
+
})] }),
|
|
2226
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", {
|
|
2227
|
+
className: detailLabelClassName,
|
|
2228
|
+
children: "Price"
|
|
2229
|
+
}), /* @__PURE__ */ jsx("dd", {
|
|
2230
|
+
className: "flex flex-wrap items-baseline gap-1",
|
|
2231
|
+
children: primaryPrice
|
|
2232
|
+
})] }),
|
|
2233
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", {
|
|
2234
|
+
className: detailLabelClassName,
|
|
2235
|
+
children: "Current period"
|
|
2236
|
+
}), /* @__PURE__ */ jsx("dd", {
|
|
2237
|
+
className: "text-foreground",
|
|
2238
|
+
children: subscription.alignment?.currentAlignedBillingPeriod ? formatDateRange(subscription.alignment.currentAlignedBillingPeriod.from, subscription.alignment.currentAlignedBillingPeriod.to) : "—"
|
|
2239
|
+
})] })
|
|
2240
|
+
]
|
|
2241
|
+
}), featureRows.length > 0 ? /* @__PURE__ */ jsx("div", {
|
|
2242
|
+
className: "space-y-5 pt-2 border-t border-border",
|
|
2243
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
2244
|
+
className: "space-y-2",
|
|
2245
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
2246
|
+
className: cn(sectionLabelClassName, "mb-5"),
|
|
2247
|
+
children: "Entitlements"
|
|
2248
|
+
}), /* @__PURE__ */ jsx("ul", {
|
|
2249
|
+
className: "space-y-3",
|
|
2250
|
+
children: featureRows.map((row) => /* @__PURE__ */ jsxs("li", {
|
|
2251
|
+
className: "grid gap-1 text-sm sm:grid-cols-4 sm:items-center sm:gap-4",
|
|
2252
|
+
children: [
|
|
2253
|
+
/* @__PURE__ */ jsx("div", {
|
|
2254
|
+
className: "flex items-start gap-2 text-muted-foreground sm:col-span-2",
|
|
2255
|
+
children: /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsxs("span", {
|
|
2256
|
+
className: "text-foreground font-medium",
|
|
2257
|
+
children: [row.name, " "]
|
|
2258
|
+
}), row.entitlementType === "static" && row.value ? `: ${row.value}` : ""] })
|
|
2259
|
+
}),
|
|
2260
|
+
/* @__PURE__ */ jsx("div", {
|
|
2261
|
+
className: "text-muted-foreground sm:text-right",
|
|
2262
|
+
children: row.entitlementType === "metered" && row.limit != null ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2263
|
+
formatNumber(row.limit),
|
|
2264
|
+
row.period ? ` / ${row.period}` : "",
|
|
2265
|
+
row.overagePrice ? /* @__PURE__ */ jsxs("div", {
|
|
2266
|
+
className: "text-xs mt-0.5",
|
|
2267
|
+
children: ["Overage: ", row.overagePrice]
|
|
2268
|
+
}) : null
|
|
2269
|
+
] }) : row.entitlementType === "static" && row.value ? row.value : "Included"
|
|
2270
|
+
}),
|
|
2271
|
+
/* @__PURE__ */ jsx("div", {
|
|
2272
|
+
className: "text-xs text-muted-foreground sm:text-right",
|
|
2273
|
+
children: formatActiveRange(row.activeFrom, row.activeTo)
|
|
2274
|
+
})
|
|
2275
|
+
]
|
|
2276
|
+
}, `${row.key}:${row.phaseId}`))
|
|
2277
|
+
})]
|
|
2278
|
+
})
|
|
2279
|
+
}) : null]
|
|
2280
|
+
})] })]
|
|
2281
|
+
});
|
|
2282
|
+
};
|
|
2081
2283
|
//#endregion
|
|
2082
2284
|
//#region src/pages/subscriptions/Usage.tsx
|
|
2083
2285
|
const isMeteredEntitlement = (entitlement) => {
|
|
@@ -2237,7 +2439,6 @@ const Usage = ({ usage, isFetching, currentItems, subscription, isPendingFirstPa
|
|
|
2237
2439
|
]
|
|
2238
2440
|
});
|
|
2239
2441
|
};
|
|
2240
|
-
|
|
2241
2442
|
//#endregion
|
|
2242
2443
|
//#region src/pages/subscriptions/ActiveSubscription.tsx
|
|
2243
2444
|
const ActiveSubscription = ({ subscription, deploymentName }) => {
|
|
@@ -2261,6 +2462,7 @@ const ActiveSubscription = ({ subscription, deploymentName }) => {
|
|
|
2261
2462
|
/* @__PURE__ */ jsx(DismissibleAlertAction, {})
|
|
2262
2463
|
]
|
|
2263
2464
|
}),
|
|
2465
|
+
/* @__PURE__ */ jsx(SubscriptionPlanDetails, { subscription }),
|
|
2264
2466
|
/* @__PURE__ */ jsx(Usage, {
|
|
2265
2467
|
currentItems: activePhase?.items,
|
|
2266
2468
|
usage: usageQuery.data,
|
|
@@ -2280,7 +2482,6 @@ const ActiveSubscription = ({ subscription, deploymentName }) => {
|
|
|
2280
2482
|
})
|
|
2281
2483
|
] });
|
|
2282
2484
|
};
|
|
2283
|
-
|
|
2284
2485
|
//#endregion
|
|
2285
2486
|
//#region src/pages/subscriptions/SubscriptionsList.tsx
|
|
2286
2487
|
const formatDate = (dateString) => {
|
|
@@ -2354,7 +2555,6 @@ const SubscriptionItem = ({ subscription, isSelected, isExpired }) => {
|
|
|
2354
2555
|
}, subscription.id)
|
|
2355
2556
|
});
|
|
2356
2557
|
};
|
|
2357
|
-
|
|
2358
2558
|
//#endregion
|
|
2359
2559
|
//#region src/pages/SubscriptionsPage.tsx
|
|
2360
2560
|
const SubscriptionsPage = () => {
|
|
@@ -2399,7 +2599,6 @@ const SubscriptionsPage = () => {
|
|
|
2399
2599
|
})]
|
|
2400
2600
|
});
|
|
2401
2601
|
};
|
|
2402
|
-
|
|
2403
2602
|
//#endregion
|
|
2404
2603
|
//#region src/pages/SubscriptionsPageSkeleton.tsx
|
|
2405
2604
|
const SubscriptionsPageSkeleton = () => /* @__PURE__ */ jsx("div", {
|
|
@@ -2433,7 +2632,6 @@ const SubscriptionsPageSkeleton = () => /* @__PURE__ */ jsx("div", {
|
|
|
2433
2632
|
]
|
|
2434
2633
|
})
|
|
2435
2634
|
});
|
|
2436
|
-
|
|
2437
2635
|
//#endregion
|
|
2438
2636
|
//#region src/ZuploMonetizationPlugin.tsx
|
|
2439
2637
|
const PRICING_PATH = "/pricing";
|
|
@@ -2520,6 +2718,5 @@ const zuploMonetizationPlugin = createPlugin((options = {}) => ({
|
|
|
2520
2718
|
];
|
|
2521
2719
|
}
|
|
2522
2720
|
}));
|
|
2523
|
-
|
|
2524
2721
|
//#endregion
|
|
2525
|
-
export { zuploMonetizationPlugin };
|
|
2722
|
+
export { 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.32",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/zuplo/zudoku",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"@testing-library/react": "16.3.2",
|
|
28
28
|
"@types/react": "19.2.14",
|
|
29
29
|
"@types/react-dom": "19.2.3",
|
|
30
|
-
"happy-dom": "20.
|
|
31
|
-
"react": "19.2.
|
|
32
|
-
"react-dom": "19.2.
|
|
33
|
-
"tsdown": "0.
|
|
34
|
-
"zudoku": "0.
|
|
30
|
+
"happy-dom": "20.9.0",
|
|
31
|
+
"react": "19.2.5",
|
|
32
|
+
"react-dom": "19.2.5",
|
|
33
|
+
"tsdown": "0.21.9",
|
|
34
|
+
"zudoku": "0.76.0"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"react": ">=19.2.0",
|