@zuplo/zudoku-plugin-monetization 0.0.46 → 0.0.47
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.
|
@@ -148,6 +148,13 @@ const formatTieredPriceBreakdown = (opts) => {
|
|
|
148
148
|
const tierHasPositivePrice = (tier) => parseFloat(tier.flatPrice?.amount ?? "0") > 0 || parseFloat(tier.unitPrice?.amount ?? "0") > 0;
|
|
149
149
|
//#endregion
|
|
150
150
|
//#region src/utils/categorizeRateCards.ts
|
|
151
|
+
const formatInlinePrice = (opts) => {
|
|
152
|
+
const unit = parseFloat(opts.unitAmount ?? "0");
|
|
153
|
+
const flat = parseFloat(opts.flatAmount ?? "0");
|
|
154
|
+
const flatPart = flat > 0 ? formatPrice(flat, opts.currency) : "";
|
|
155
|
+
const unitPart = unit > 0 ? `${formatPrice(unit, opts.currency)}/${opts.unitLabel}` : "";
|
|
156
|
+
return (flatPart && unitPart ? `${flatPart} + ${unitPart}` : flatPart || unitPart) || void 0;
|
|
157
|
+
};
|
|
151
158
|
const categorizeRateCards = (rateCards, options) => {
|
|
152
159
|
const { currency, units, planBillingCadence } = options ?? {};
|
|
153
160
|
const quotas = [];
|
|
@@ -163,8 +170,10 @@ const categorizeRateCards = (rateCards, options) => {
|
|
|
163
170
|
return "month";
|
|
164
171
|
};
|
|
165
172
|
const firstTier = rc.price?.type === "tiered" && rc.price.tiers.length > 0 ? rc.price.tiers[0] : void 0;
|
|
166
|
-
const
|
|
167
|
-
|
|
173
|
+
const billsFromFirstUnit = !!firstTier && tierHasPositivePrice(firstTier) || rc.price?.type === "unit" && parseFloat(rc.price.amount) > 0;
|
|
174
|
+
const includedQuota = et.type === "metered" ? et.issueAfterReset ?? 0 : 0;
|
|
175
|
+
const isHardCap = et.type === "metered" && et.isSoftLimit === false;
|
|
176
|
+
if (et.type === "metered" && (isHardCap || includedQuota > 0 && !billsFromFirstUnit)) {
|
|
168
177
|
let tierPrices;
|
|
169
178
|
if (rc.price?.type === "tiered" && rc.price.tiers) tierPrices = formatTieredPriceBreakdown({
|
|
170
179
|
tiers: rc.price.tiers.map((t) => ({
|
|
@@ -176,12 +185,24 @@ const categorizeRateCards = (rateCards, options) => {
|
|
|
176
185
|
unitLabel: unitLabelFor(rc),
|
|
177
186
|
includedLabel: "Included"
|
|
178
187
|
});
|
|
188
|
+
const inlinePrice = rc.price?.type === "unit" ? formatInlinePrice({
|
|
189
|
+
unitAmount: rc.price.amount,
|
|
190
|
+
currency,
|
|
191
|
+
unitLabel: unitLabelFor(rc)
|
|
192
|
+
}) : rc.price?.type === "tiered" && rc.price.tiers.length === 1 ? formatInlinePrice({
|
|
193
|
+
flatAmount: rc.price.tiers[0].flatPrice?.amount,
|
|
194
|
+
unitAmount: rc.price.tiers[0].unitPrice?.amount,
|
|
195
|
+
currency,
|
|
196
|
+
unitLabel: unitLabelFor(rc)
|
|
197
|
+
}) : void 0;
|
|
179
198
|
quotas.push({
|
|
180
199
|
key: rc.featureKey ?? rc.key,
|
|
181
200
|
name: rc.name,
|
|
182
|
-
limit:
|
|
201
|
+
limit: includedQuota,
|
|
183
202
|
period: periodFor(rc),
|
|
184
|
-
tierPrices
|
|
203
|
+
tierPrices,
|
|
204
|
+
...inlinePrice ? { unitPrice: inlinePrice } : {},
|
|
205
|
+
...isHardCap ? { isHardCap: true } : {}
|
|
185
206
|
});
|
|
186
207
|
} else if (et.type === "metered" && rc.type === "usage_based" && rc.price) {
|
|
187
208
|
const unitLabel = unitLabelFor(rc);
|
|
@@ -195,11 +216,12 @@ const categorizeRateCards = (rateCards, options) => {
|
|
|
195
216
|
continue;
|
|
196
217
|
}
|
|
197
218
|
if (tiers.length === 1) {
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
219
|
+
const pricePart = formatInlinePrice({
|
|
220
|
+
flatAmount: tiers[0].flatPrice?.amount,
|
|
221
|
+
unitAmount: tiers[0].unitPrice?.amount,
|
|
222
|
+
currency,
|
|
223
|
+
unitLabel
|
|
224
|
+
});
|
|
203
225
|
if (pricePart) {
|
|
204
226
|
quotas.push({
|
|
205
227
|
key: rc.featureKey ?? rc.key,
|
|
@@ -249,7 +271,11 @@ const categorizeRateCards = (rateCards, options) => {
|
|
|
249
271
|
key: rc.featureKey ?? rc.key,
|
|
250
272
|
name: rc.name
|
|
251
273
|
});
|
|
252
|
-
} else if (et.type === "
|
|
274
|
+
} else if (et.type === "metered") features.push({
|
|
275
|
+
key: rc.featureKey ?? rc.key,
|
|
276
|
+
name: rc.name
|
|
277
|
+
});
|
|
278
|
+
else if (et.type === "boolean") features.push({
|
|
253
279
|
key: rc.featureKey ?? rc.key,
|
|
254
280
|
name: rc.name
|
|
255
281
|
});
|
|
@@ -520,7 +546,7 @@ const FeatureItem = ({ feature, className }) => {
|
|
|
520
546
|
//#region src/pricing-ui/QuotaItem.tsx
|
|
521
547
|
const QuotaItem = ({ quota, className }) => {
|
|
522
548
|
const hasTierBreakdown = !!quota.tierPrices && quota.tierPrices.length > 0;
|
|
523
|
-
const showQuotaLine = !quota.isPayg && !hasTierBreakdown;
|
|
549
|
+
const showQuotaLine = !quota.isPayg && (quota.isHardCap || !hasTierBreakdown);
|
|
524
550
|
return /* @__PURE__ */ jsxs("div", {
|
|
525
551
|
className: cn("flex items-start gap-2", className),
|
|
526
552
|
children: [/* @__PURE__ */ jsx(CheckIcon, { className: "w-4 h-4 text-primary shrink-0 mt-0.5" }), /* @__PURE__ */ jsxs("div", {
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { D as formatDurationAdjective, E as formatDuration, O as formatDurationInterval, T as formatPrice, _ as formatPlanPrice, a as planHasDefaultTaxBehavior, b as sameEntitlementSet, c as getPlanPriceSchedule, d as PlanEntitlements, f as PlanPhaseHeader, l as PlanPriceTag, o as subscriptionTaxLegendSentence, p as EntitlementList, r as isCustomPlan, t as PricingTable, u as PlanPriceSchedule, w as formatMinorCurrencyAmount, x as categorizeRateCards, y as comparePlanEntitlements } from "./PricingTable-
|
|
1
|
+
import { D as formatDurationAdjective, E as formatDuration, O as formatDurationInterval, T as formatPrice, _ as formatPlanPrice, a as planHasDefaultTaxBehavior, b as sameEntitlementSet, c as getPlanPriceSchedule, d as PlanEntitlements, f as PlanPhaseHeader, l as PlanPriceTag, o as subscriptionTaxLegendSentence, p as EntitlementList, r as isCustomPlan, t as PricingTable, u as PlanPriceSchedule, w as formatMinorCurrencyAmount, x as categorizeRateCards, y as comparePlanEntitlements } from "./PricingTable-Ec-BAGAS.mjs";
|
|
2
2
|
import { cn, createPlugin, joinUrl, throwIfProblemJson } from "zudoku";
|
|
3
3
|
import { AlertTriangleIcon, ArrowDownIcon, ArrowLeftRightIcon, ArrowUpIcon, BadgePercentIcon, CalendarIcon, CheckCheckIcon, CheckIcon, ChevronDownIcon, CircleSlashIcon, ClockIcon, CreditCardIcon, Grid2x2XIcon, HistoryIcon, InfoIcon, Loader2Icon, LockIcon, MoreVerticalIcon, RefreshCcw, RefreshCwIcon, Settings, ShieldIcon, StarsIcon, Trash2Icon, XIcon } from "zudoku/icons";
|
|
4
4
|
import { Link, Navigate, Outlet, useLocation, useNavigate, useSearchParams } from "zudoku/router";
|
|
5
5
|
import { useAuth, useZudoku } from "zudoku/hooks";
|
|
6
6
|
import { QueryClient, QueryClientProvider, queryOptions, useMutation, useQuery, useQueryClient, useSuspenseQuery } from "zudoku/react-query";
|
|
7
|
-
import { Button, ClientOnly, Head, Heading, Link as Link$1, Slot } from "zudoku/components";
|
|
7
|
+
import { Button, ClientOnly, Head, Heading, Link as Link$1, Slot, ZudokuError } from "zudoku/components";
|
|
8
8
|
import { createContext, use, useEffect, useMemo, useState } from "react";
|
|
9
9
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
10
10
|
import { Alert, AlertAction, AlertDescription, AlertTitle } from "zudoku/ui/Alert";
|
|
@@ -40,11 +40,27 @@ const isTaxInclusiveFromPurchaseDetails = (response) => {
|
|
|
40
40
|
return response.tax?.taxInclusive === true;
|
|
41
41
|
};
|
|
42
42
|
//#endregion
|
|
43
|
+
//#region src/deploymentName.ts
|
|
44
|
+
/**
|
|
45
|
+
* Resolves the Zuplo deployment name from the environment.
|
|
46
|
+
*
|
|
47
|
+
* `ZUPLO_PUBLIC_DEPLOYMENT_NAME` is populated by `zuplo link`, so a missing
|
|
48
|
+
* value almost always means the project hasn't been linked yet (the common
|
|
49
|
+
* case when running the docs locally). Throw a `ZudokuError` with actionable
|
|
50
|
+
* guidance rather than a bare "not set" so the UI can surface the fix.
|
|
51
|
+
*/
|
|
52
|
+
const resolveDeploymentName = (deploymentName) => {
|
|
53
|
+
const trimmed = deploymentName?.trim();
|
|
54
|
+
if (!trimmed) throw new ZudokuError("This project is not linked to a Zuplo deployment, so the monetization plugin can't load pricing or subscriptions.", {
|
|
55
|
+
title: "Not linked to a Zuplo deployment",
|
|
56
|
+
developerHint: "Run `zuplo link` to connect this project to your Zuplo deployment. This sets the `ZUPLO_PUBLIC_DEPLOYMENT_NAME` environment variable that the monetization plugin needs."
|
|
57
|
+
});
|
|
58
|
+
return trimmed;
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
43
61
|
//#region src/hooks/useDeploymentName.ts
|
|
44
62
|
const useDeploymentName = () => {
|
|
45
|
-
|
|
46
|
-
if (!deploymentName) throw new Error("ZUPLO_PUBLIC_DEPLOYMENT_NAME is not set");
|
|
47
|
-
return deploymentName;
|
|
63
|
+
return resolveDeploymentName(useZudoku().env.ZUPLO_PUBLIC_DEPLOYMENT_NAME);
|
|
48
64
|
};
|
|
49
65
|
//#endregion
|
|
50
66
|
//#region src/hooks/usePurchaseDetails.ts
|
|
@@ -550,11 +566,7 @@ const ManagePaymentPage = () => {
|
|
|
550
566
|
};
|
|
551
567
|
//#endregion
|
|
552
568
|
//#region src/queries.ts
|
|
553
|
-
const getDeploymentName = (context) =>
|
|
554
|
-
const deploymentName = context.env.ZUPLO_PUBLIC_DEPLOYMENT_NAME;
|
|
555
|
-
if (!deploymentName) throw new Error("ZUPLO_PUBLIC_DEPLOYMENT_NAME is not set");
|
|
556
|
-
return deploymentName;
|
|
557
|
-
};
|
|
569
|
+
const getDeploymentName = (context) => resolveDeploymentName(context.env.ZUPLO_PUBLIC_DEPLOYMENT_NAME);
|
|
558
570
|
const pricingPageQuery = (context) => queryOptions({
|
|
559
571
|
queryKey: [`/v3/zudoku-metering/${getDeploymentName(context)}/pricing-page`],
|
|
560
572
|
meta: { context: context.getAuthState().isAuthenticated ? context : void 0 }
|
package/dist/pricing-ui.d.mts
CHANGED
package/dist/pricing-ui.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as formatStaticEntitlementConfig, D as formatDurationAdjective, E as formatDuration, O as formatDurationInterval, S as formatTieredPriceBreakdown, T as formatPrice, _ as formatPlanPrice, a as planHasDefaultTaxBehavior, c as getPlanPriceSchedule, d as PlanEntitlements, g as getPhasePriceLabel, h as FeatureItem, i as collectDefaultTaxBehaviors, l as PlanPriceTag, m as QuotaItem, n as PricingCard, o as subscriptionTaxLegendSentence, s as taxBehaviorLegendSentence, t as PricingTable, u as PlanPriceSchedule, v as getPlanPrice, w as formatMinorCurrencyAmount, x as categorizeRateCards } from "./PricingTable-
|
|
1
|
+
import { C as formatStaticEntitlementConfig, D as formatDurationAdjective, E as formatDuration, O as formatDurationInterval, S as formatTieredPriceBreakdown, T as formatPrice, _ as formatPlanPrice, a as planHasDefaultTaxBehavior, c as getPlanPriceSchedule, d as PlanEntitlements, g as getPhasePriceLabel, h as FeatureItem, i as collectDefaultTaxBehaviors, l as PlanPriceTag, m as QuotaItem, n as PricingCard, o as subscriptionTaxLegendSentence, s as taxBehaviorLegendSentence, t as PricingTable, u as PlanPriceSchedule, v as getPlanPrice, w as formatMinorCurrencyAmount, x as categorizeRateCards } from "./PricingTable-Ec-BAGAS.mjs";
|
|
2
2
|
export { FeatureItem, PlanEntitlements, PlanPriceSchedule, PlanPriceTag, PricingCard, PricingTable, QuotaItem, categorizeRateCards, collectDefaultTaxBehaviors, formatDuration, formatDurationAdjective, formatDurationInterval, formatMinorCurrencyAmount, formatPlanPrice, formatPrice, formatStaticEntitlementConfig, formatTieredPriceBreakdown, getPhasePriceLabel, getPlanPrice, getPlanPriceSchedule, planHasDefaultTaxBehavior, subscriptionTaxLegendSentence, taxBehaviorLegendSentence };
|