@zuplo/zudoku-plugin-monetization 0.0.39 → 0.0.41
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.
|
@@ -15,6 +15,12 @@ const formatDuration = (iso) => {
|
|
|
15
15
|
if (d.weeks && d.weeks > 1) return `${d.weeks} weeks`;
|
|
16
16
|
if (d.days === 1) return "day";
|
|
17
17
|
if (d.days && d.days > 1) return `${d.days} days`;
|
|
18
|
+
if (d.hours === 1) return "hour";
|
|
19
|
+
if (d.hours && d.hours > 1) return `${d.hours} hours`;
|
|
20
|
+
if (d.minutes === 1) return "minute";
|
|
21
|
+
if (d.minutes && d.minutes > 1) return `${d.minutes} minutes`;
|
|
22
|
+
if (d.seconds === 1) return "second";
|
|
23
|
+
if (d.seconds && d.seconds > 1) return `${d.seconds} seconds`;
|
|
18
24
|
return iso;
|
|
19
25
|
} catch {
|
|
20
26
|
return iso;
|
|
@@ -31,6 +37,12 @@ const formatDurationInterval = (iso) => {
|
|
|
31
37
|
if (d.weeks && d.weeks > 1) return `every ${d.weeks} weeks`;
|
|
32
38
|
if (d.days === 1) return "daily";
|
|
33
39
|
if (d.days && d.days > 1) return `every ${d.days} days`;
|
|
40
|
+
if (d.hours === 1) return "hourly";
|
|
41
|
+
if (d.hours && d.hours > 1) return `every ${d.hours} hours`;
|
|
42
|
+
if (d.minutes === 1) return "every minute";
|
|
43
|
+
if (d.minutes && d.minutes > 1) return `every ${d.minutes} minutes`;
|
|
44
|
+
if (d.seconds === 1) return "every second";
|
|
45
|
+
if (d.seconds && d.seconds > 1) return `every ${d.seconds} seconds`;
|
|
34
46
|
return iso;
|
|
35
47
|
} catch {
|
|
36
48
|
return iso;
|
|
@@ -40,7 +52,8 @@ const formatDurationInterval = (iso) => {
|
|
|
40
52
|
* Returns an adjective form suitable for possessive context
|
|
41
53
|
* e.g. "your monthly quota", "your weekly limit".
|
|
42
54
|
* Falls back to "billing period" for multi-unit cadences
|
|
43
|
-
* where
|
|
55
|
+
* or sub-hour units where the adjective form is grammatically awkward
|
|
56
|
+
* (e.g. "every 3 months", "every 5 minutes").
|
|
44
57
|
*/
|
|
45
58
|
const formatDurationAdjective = (iso) => {
|
|
46
59
|
try {
|
|
@@ -49,6 +62,7 @@ const formatDurationAdjective = (iso) => {
|
|
|
49
62
|
if (d.months === 1) return "monthly";
|
|
50
63
|
if (d.weeks === 1) return "weekly";
|
|
51
64
|
if (d.days === 1) return "daily";
|
|
65
|
+
if (d.hours === 1) return "hourly";
|
|
52
66
|
return "billing period";
|
|
53
67
|
} catch {
|
|
54
68
|
return "billing period";
|
|
@@ -104,7 +118,7 @@ const parseAmount = (value) => {
|
|
|
104
118
|
return Number.isFinite(parsed) ? parsed : void 0;
|
|
105
119
|
};
|
|
106
120
|
const formatTieredPriceBreakdown = (opts) => {
|
|
107
|
-
const { tiers, currency, unitLabel, includedLabel
|
|
121
|
+
const { tiers, currency, unitLabel, includedLabel } = opts;
|
|
108
122
|
if (!tiers || tiers.length <= 1) return;
|
|
109
123
|
const lines = [];
|
|
110
124
|
let lastUpTo;
|
|
@@ -113,10 +127,10 @@ const formatTieredPriceBreakdown = (opts) => {
|
|
|
113
127
|
const unit = parseAmount(tier.unitPriceAmount) ?? 0;
|
|
114
128
|
const flat = parseAmount(tier.flatPriceAmount) ?? 0;
|
|
115
129
|
const prefix = upTo != null ? `Up to ${upTo.toLocaleString("en-US")}` : lastUpTo != null ? `Over ${lastUpTo.toLocaleString("en-US")}` : `Per ${unitLabel}`;
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
const
|
|
119
|
-
|
|
130
|
+
const flatPart = flat > 0 ? formatPrice(flat, currency) : "";
|
|
131
|
+
const unitPart = unit > 0 ? `${formatPrice(unit, currency)}/${unitLabel}` : "";
|
|
132
|
+
const pricePart = flatPart && unitPart ? `${flatPart} + ${unitPart}` : flatPart || unitPart || includedLabel;
|
|
133
|
+
lines.push(`${prefix}: ${pricePart}`);
|
|
120
134
|
if (upTo != null) lastUpTo = upTo;
|
|
121
135
|
}
|
|
122
136
|
return lines.length > 0 ? lines : void 0;
|
|
@@ -130,32 +144,99 @@ const categorizeRateCards = (rateCards, options) => {
|
|
|
130
144
|
for (const rc of rateCards) {
|
|
131
145
|
const et = rc.entitlementTemplate;
|
|
132
146
|
if (!et) continue;
|
|
133
|
-
|
|
134
|
-
|
|
147
|
+
const unitLabelFor = (rcArg) => units?.[rcArg.key] ?? units?.[rcArg.featureKey ?? ""] ?? "unit";
|
|
148
|
+
const periodFor = (rcArg) => {
|
|
149
|
+
if (et.type === "metered" && et.usagePeriod) return formatDuration(et.usagePeriod);
|
|
150
|
+
if (rcArg.billingCadence) return formatDuration(rcArg.billingCadence);
|
|
151
|
+
if (planBillingCadence) return formatDuration(planBillingCadence);
|
|
152
|
+
return "month";
|
|
153
|
+
};
|
|
154
|
+
const firstTier = rc.price?.type === "tiered" && rc.price.tiers.length > 0 ? rc.price.tiers[0] : void 0;
|
|
155
|
+
const firstTierIsPriced = !!firstTier && (parseFloat(firstTier.flatPrice?.amount ?? "0") > 0 || parseFloat(firstTier.unitPrice?.amount ?? "0") > 0);
|
|
156
|
+
if (et.type === "metered" && et.issueAfterReset != null && !firstTierIsPriced) {
|
|
135
157
|
let tierPrices;
|
|
136
|
-
if (rc.price?.type === "tiered" && rc.price.tiers) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
158
|
+
if (rc.price?.type === "tiered" && rc.price.tiers) tierPrices = formatTieredPriceBreakdown({
|
|
159
|
+
tiers: rc.price.tiers.map((t) => ({
|
|
160
|
+
upToAmount: t.upToAmount,
|
|
161
|
+
unitPriceAmount: t.unitPrice?.amount,
|
|
162
|
+
flatPriceAmount: t.flatPrice?.amount
|
|
163
|
+
})),
|
|
164
|
+
currency,
|
|
165
|
+
unitLabel: unitLabelFor(rc),
|
|
166
|
+
includedLabel: "Included"
|
|
167
|
+
});
|
|
168
|
+
quotas.push({
|
|
169
|
+
key: rc.featureKey ?? rc.key,
|
|
170
|
+
name: rc.name,
|
|
171
|
+
limit: et.issueAfterReset,
|
|
172
|
+
period: periodFor(rc),
|
|
173
|
+
tierPrices
|
|
174
|
+
});
|
|
175
|
+
} else if (et.type === "metered" && rc.type === "usage_based" && rc.price) {
|
|
176
|
+
const unitLabel = unitLabelFor(rc);
|
|
177
|
+
if (rc.price.type === "tiered" && rc.price.tiers.length > 0) {
|
|
178
|
+
const tiers = rc.price.tiers;
|
|
179
|
+
if (!tiers.some((t) => parseFloat(t.flatPrice?.amount ?? "0") > 0 || parseFloat(t.unitPrice?.amount ?? "0") > 0)) {
|
|
180
|
+
features.push({
|
|
181
|
+
key: rc.featureKey ?? rc.key,
|
|
182
|
+
name: rc.name
|
|
183
|
+
});
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (tiers.length === 1) {
|
|
187
|
+
const unit = parseFloat(tiers[0].unitPrice?.amount ?? "0");
|
|
188
|
+
const flat = parseFloat(tiers[0].flatPrice?.amount ?? "0");
|
|
189
|
+
const flatPart = flat > 0 ? formatPrice(flat, currency) : "";
|
|
190
|
+
const unitPart = unit > 0 ? `${formatPrice(unit, currency)}/${unitLabel}` : "";
|
|
191
|
+
const pricePart = flatPart && unitPart ? `${flatPart} + ${unitPart}` : flatPart || unitPart;
|
|
192
|
+
if (pricePart) {
|
|
193
|
+
quotas.push({
|
|
194
|
+
key: rc.featureKey ?? rc.key,
|
|
195
|
+
name: rc.name,
|
|
196
|
+
limit: 0,
|
|
197
|
+
period: periodFor(rc),
|
|
198
|
+
unitPrice: pricePart,
|
|
199
|
+
isPayg: true
|
|
200
|
+
});
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
features.push({
|
|
204
|
+
key: rc.featureKey ?? rc.key,
|
|
205
|
+
name: rc.name
|
|
206
|
+
});
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const tierPrices = formatTieredPriceBreakdown({
|
|
210
|
+
tiers: tiers.map((t) => ({
|
|
140
211
|
upToAmount: t.upToAmount,
|
|
141
212
|
unitPriceAmount: t.unitPrice?.amount,
|
|
142
213
|
flatPriceAmount: t.flatPrice?.amount
|
|
143
214
|
})),
|
|
144
215
|
currency,
|
|
145
216
|
unitLabel,
|
|
146
|
-
includedLabel: "Included"
|
|
147
|
-
omitIncludedUpToAmount: et.issueAfterReset
|
|
217
|
+
includedLabel: "Included"
|
|
148
218
|
});
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
219
|
+
quotas.push({
|
|
220
|
+
key: rc.featureKey ?? rc.key,
|
|
221
|
+
name: rc.name,
|
|
222
|
+
limit: 0,
|
|
223
|
+
period: periodFor(rc),
|
|
224
|
+
tierPrices,
|
|
225
|
+
isPayg: true
|
|
226
|
+
});
|
|
227
|
+
} else if (rc.price.type === "unit" && parseFloat(rc.price.amount) > 0) {
|
|
228
|
+
const amount = parseFloat(rc.price.amount);
|
|
229
|
+
quotas.push({
|
|
230
|
+
key: rc.featureKey ?? rc.key,
|
|
231
|
+
name: rc.name,
|
|
232
|
+
limit: 0,
|
|
233
|
+
period: periodFor(rc),
|
|
234
|
+
unitPrice: `${formatPrice(amount, currency)}/${unitLabel}`,
|
|
235
|
+
isPayg: true
|
|
236
|
+
});
|
|
237
|
+
} else features.push({
|
|
153
238
|
key: rc.featureKey ?? rc.key,
|
|
154
|
-
name: rc.name
|
|
155
|
-
limit: et.issueAfterReset,
|
|
156
|
-
period: rc.billingCadence ? formatDuration(rc.billingCadence) : planBillingCadence ? formatDuration(planBillingCadence) : "month",
|
|
157
|
-
overagePrice,
|
|
158
|
-
tierPrices
|
|
239
|
+
name: rc.name
|
|
159
240
|
});
|
|
160
241
|
} else if (et.type === "boolean") features.push({
|
|
161
242
|
key: rc.featureKey ?? rc.key,
|
|
@@ -217,30 +298,33 @@ const FeatureItem = ({ feature, className }) => {
|
|
|
217
298
|
//#endregion
|
|
218
299
|
//#region src/pricing-ui/QuotaItem.tsx
|
|
219
300
|
const QuotaItem = ({ quota, className }) => {
|
|
301
|
+
const hasTierBreakdown = !!quota.tierPrices && quota.tierPrices.length > 0;
|
|
302
|
+
const showQuotaLine = !quota.isPayg && !hasTierBreakdown;
|
|
220
303
|
return /* @__PURE__ */ jsxs("div", {
|
|
221
304
|
className: cn("flex items-start gap-2", className),
|
|
222
305
|
children: [/* @__PURE__ */ jsx(CheckIcon, { className: "w-4 h-4 text-primary shrink-0 mt-0.5" }), /* @__PURE__ */ jsxs("div", {
|
|
223
306
|
className: "text-sm",
|
|
224
307
|
children: [
|
|
225
|
-
/* @__PURE__ */ jsxs(
|
|
308
|
+
showQuotaLine ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
309
|
+
/* @__PURE__ */ jsxs("span", {
|
|
310
|
+
className: "font-medium",
|
|
311
|
+
children: [quota.name, ":"]
|
|
312
|
+
}),
|
|
313
|
+
" ",
|
|
314
|
+
quota.limit.toLocaleString(),
|
|
315
|
+
" / ",
|
|
316
|
+
quota.period
|
|
317
|
+
] }) : /* @__PURE__ */ jsx("span", {
|
|
226
318
|
className: "font-medium",
|
|
227
|
-
children:
|
|
319
|
+
children: quota.name
|
|
228
320
|
}),
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
quota.period,
|
|
233
|
-
quota.tierPrices && quota.tierPrices.length > 0 && /* @__PURE__ */ jsx("ul", {
|
|
234
|
-
className: "text-xs text-muted-foreground mt-1 space-y-0.5",
|
|
235
|
-
children: quota.tierPrices.map((line) => /* @__PURE__ */ jsx("li", { children: line }, line))
|
|
321
|
+
quota.unitPrice && /* @__PURE__ */ jsxs("span", {
|
|
322
|
+
className: "text-muted-foreground",
|
|
323
|
+
children: [" — ", quota.unitPrice]
|
|
236
324
|
}),
|
|
237
|
-
|
|
238
|
-
className: "text-xs text-muted-foreground mt-0.5",
|
|
239
|
-
children:
|
|
240
|
-
"+",
|
|
241
|
-
quota.overagePrice,
|
|
242
|
-
" after quota"
|
|
243
|
-
]
|
|
325
|
+
hasTierBreakdown && /* @__PURE__ */ jsx("ul", {
|
|
326
|
+
className: "text-xs text-muted-foreground mt-1 space-y-0.5",
|
|
327
|
+
children: quota.tierPrices?.map((line) => /* @__PURE__ */ jsx("li", { children: line }, line))
|
|
244
328
|
})
|
|
245
329
|
]
|
|
246
330
|
})]
|
|
@@ -410,11 +494,40 @@ const subscriptionTaxLegendSentence = (behavior) => {
|
|
|
410
494
|
}
|
|
411
495
|
};
|
|
412
496
|
//#endregion
|
|
497
|
+
//#region src/utils/formatPlanPrice.ts
|
|
498
|
+
const isPricedUsageRateCard = (rc) => {
|
|
499
|
+
if (rc.type !== "usage_based" || !rc.price) return false;
|
|
500
|
+
const p = rc.price;
|
|
501
|
+
if (p.type === "unit") return parseFloat(p.amount) > 0;
|
|
502
|
+
if (p.type === "tiered") return p.tiers.some((t) => parseFloat(t.flatPrice?.amount ?? "0") > 0 || parseFloat(t.unitPrice?.amount ?? "0") > 0);
|
|
503
|
+
return true;
|
|
504
|
+
};
|
|
505
|
+
const hasPricedUsageRateCard = (plan) => plan.phases.some((phase) => phase.rateCards.some(isPricedUsageRateCard));
|
|
506
|
+
/**
|
|
507
|
+
* Headline pricing for plan cards. Centralizes the "Pay as you go" detection:
|
|
508
|
+
* plans whose flat-fee total is zero but that bill on usage shouldn't render
|
|
509
|
+
* as "Free" - they're charged per-unit.
|
|
510
|
+
*/
|
|
511
|
+
const formatPlanPrice = (plan) => {
|
|
512
|
+
if (plan.phases.length === 0) return { type: "free" };
|
|
513
|
+
const { monthly, yearly } = getPriceFromPlan(plan);
|
|
514
|
+
if (monthly > 0) return {
|
|
515
|
+
type: "priced",
|
|
516
|
+
monthly,
|
|
517
|
+
yearly
|
|
518
|
+
};
|
|
519
|
+
if (hasPricedUsageRateCard(plan)) return {
|
|
520
|
+
type: "payg",
|
|
521
|
+
main: "Pay as you go",
|
|
522
|
+
sub: "Usage-based pricing"
|
|
523
|
+
};
|
|
524
|
+
return { type: "free" };
|
|
525
|
+
};
|
|
526
|
+
//#endregion
|
|
413
527
|
//#region src/pricing-ui/PricingCard.tsx
|
|
414
528
|
const PricingCard = ({ plan, isPopular = false, showYearlyPrice = true, units, action, className }) => {
|
|
415
529
|
if (plan.phases.length === 0) return null;
|
|
416
|
-
const
|
|
417
|
-
const isFree = price.monthly === 0;
|
|
530
|
+
const priceLabel = formatPlanPrice(plan);
|
|
418
531
|
const isCustom = plan.metadata?.isCustom === true;
|
|
419
532
|
const billingInterval = formatDuration(plan.billingCadence);
|
|
420
533
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -442,16 +555,29 @@ const PricingCard = ({ plan, isPopular = false, showYearlyPrice = true, units, a
|
|
|
442
555
|
}), /* @__PURE__ */ jsx("div", {
|
|
443
556
|
className: "text-sm text-muted-foreground mt-1",
|
|
444
557
|
children: "Contact Sales"
|
|
445
|
-
})] }) : /* @__PURE__ */ jsxs(
|
|
558
|
+
})] }) : priceLabel.type === "payg" ? /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
|
|
559
|
+
className: "text-2xl font-bold text-card-foreground text-balance",
|
|
560
|
+
children: priceLabel.main
|
|
561
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
562
|
+
className: "text-sm text-muted-foreground mt-1",
|
|
563
|
+
children: priceLabel.sub
|
|
564
|
+
})] }) : priceLabel.type === "free" ? /* @__PURE__ */ jsx("span", {
|
|
446
565
|
className: "text-3xl font-bold text-card-foreground",
|
|
447
|
-
children:
|
|
448
|
-
})
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
566
|
+
children: "Free"
|
|
567
|
+
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
568
|
+
/* @__PURE__ */ jsx("span", {
|
|
569
|
+
className: "text-3xl font-bold text-card-foreground",
|
|
570
|
+
children: formatPrice(priceLabel.monthly, plan.currency)
|
|
571
|
+
}),
|
|
572
|
+
/* @__PURE__ */ jsxs("span", {
|
|
573
|
+
className: "text-muted-foreground text-sm",
|
|
574
|
+
children: ["/", billingInterval]
|
|
575
|
+
}),
|
|
576
|
+
showYearlyPrice && priceLabel.yearly > 0 && /* @__PURE__ */ jsxs("div", {
|
|
577
|
+
className: "w-full text-sm text-muted-foreground mt-1",
|
|
578
|
+
children: [formatPrice(priceLabel.yearly, plan.currency), "/year"]
|
|
579
|
+
})
|
|
580
|
+
] })
|
|
455
581
|
}),
|
|
456
582
|
plan.paymentRequired === false && /* @__PURE__ */ jsx("div", {
|
|
457
583
|
className: "text-sm text-muted-foreground mt-1",
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as formatDuration, a as subscriptionTaxLegendSentence, c as getPriceFromPlan, f as categorizeRateCards, g as formatPrice, h as formatMinorCurrencyAmount, i as planHasDefaultTaxBehavior, l as PlanEntitlements, m as formatStaticEntitlementConfig, p as formatTieredPriceBreakdown, t as PricingTable, v as formatDurationAdjective, y as formatDurationInterval } from "./PricingTable-
|
|
1
|
+
import { _ as formatDuration, a as subscriptionTaxLegendSentence, c as getPriceFromPlan, f as categorizeRateCards, g as formatPrice, h as formatMinorCurrencyAmount, i as planHasDefaultTaxBehavior, l as PlanEntitlements, m as formatStaticEntitlementConfig, p as formatTieredPriceBreakdown, t as PricingTable, v as formatDurationAdjective, y as formatDurationInterval } from "./PricingTable-DNop2iX9.mjs";
|
|
2
2
|
import { cn, createPlugin, joinUrl, throwIfProblemJson } from "zudoku";
|
|
3
3
|
import { AlertTriangleIcon, ArrowDownIcon, ArrowLeftRightIcon, ArrowUpIcon, CalendarIcon, CheckCheckIcon, CheckIcon, CircleSlashIcon, ClockIcon, CreditCardIcon, Grid2x2XIcon, InfoIcon, Loader2Icon, LockIcon, MoreVerticalIcon, RefreshCcw, RefreshCwIcon, Settings, ShieldIcon, StarsIcon, Trash2Icon, XIcon } from "zudoku/icons";
|
|
4
4
|
import { Button, ClientOnly, Head, Heading, Link, Slot } from "zudoku/components";
|
|
@@ -1789,21 +1789,6 @@ const formatDate$1 = (dateString) => {
|
|
|
1789
1789
|
};
|
|
1790
1790
|
const formatDateRange = (from, to) => `${formatDate$1(from)} – ${formatDate$1(to)}`;
|
|
1791
1791
|
const formatNumber = (value) => value.toLocaleString("en-US");
|
|
1792
|
-
const getOveragePriceFromItem = (item, currency, units) => {
|
|
1793
|
-
const tiers = item.price?.tiers;
|
|
1794
|
-
if (!tiers || tiers.length === 0) return void 0;
|
|
1795
|
-
const amount = tiers.find((t) => {
|
|
1796
|
-
const amount = t.unitPrice?.amount;
|
|
1797
|
-
if (!amount) return false;
|
|
1798
|
-
const parsed = parseFloat(amount);
|
|
1799
|
-
return Number.isFinite(parsed) && parsed > 0;
|
|
1800
|
-
})?.unitPrice?.amount;
|
|
1801
|
-
if (!amount) return void 0;
|
|
1802
|
-
const parsed = parseFloat(amount);
|
|
1803
|
-
if (!Number.isFinite(parsed) || parsed <= 0) return void 0;
|
|
1804
|
-
const unitLabel = units?.[item.key] ?? units?.[item.featureKey] ?? "unit";
|
|
1805
|
-
return `${formatPrice(parsed, currency)}/${unitLabel}`;
|
|
1806
|
-
};
|
|
1807
1792
|
const getTierPricesFromItem = (item, currency, units) => {
|
|
1808
1793
|
if (item.price?.type !== "tiered") return;
|
|
1809
1794
|
const tiers = item.price.tiers;
|
|
@@ -1817,25 +1802,32 @@ const getTierPricesFromItem = (item, currency, units) => {
|
|
|
1817
1802
|
})),
|
|
1818
1803
|
currency,
|
|
1819
1804
|
unitLabel,
|
|
1820
|
-
includedLabel: "Included"
|
|
1821
|
-
omitIncludedUpToAmount: item.included?.entitlement?.issueAfterReset
|
|
1805
|
+
includedLabel: "Included"
|
|
1822
1806
|
});
|
|
1823
1807
|
};
|
|
1808
|
+
const hasPricedFirstTier = (item) => {
|
|
1809
|
+
const firstTier = item.price?.tiers?.[0];
|
|
1810
|
+
if (!firstTier) return false;
|
|
1811
|
+
const flat = parseFloat(firstTier.flatPrice?.amount ?? "0");
|
|
1812
|
+
const unit = parseFloat(firstTier.unitPrice?.amount ?? "0");
|
|
1813
|
+
return flat > 0 || unit > 0;
|
|
1814
|
+
};
|
|
1824
1815
|
const getEntitlementsFromItems = (items, currency, units, fallbackBillingCadence) => {
|
|
1825
1816
|
const features = [];
|
|
1826
1817
|
for (const item of items) {
|
|
1827
1818
|
const entitlement = item.included?.entitlement;
|
|
1828
1819
|
if (!entitlement) continue;
|
|
1829
1820
|
if (entitlement.type === "metered" && entitlement.issueAfterReset != null) {
|
|
1830
|
-
const cadence = item.billingCadence ?? fallbackBillingCadence;
|
|
1821
|
+
const cadence = entitlement.usagePeriod?.intervalISO ?? item.billingCadence ?? fallbackBillingCadence;
|
|
1822
|
+
const tierPrices = getTierPricesFromItem(item, currency, units);
|
|
1823
|
+
const suppressLimit = hasPricedFirstTier(item) && !!tierPrices && tierPrices.length > 0;
|
|
1831
1824
|
features.push({
|
|
1832
1825
|
entitlementType: "metered",
|
|
1833
1826
|
key: item.featureKey ?? item.key,
|
|
1834
1827
|
name: item.name ?? item.featureKey ?? item.key,
|
|
1835
|
-
limit: entitlement.issueAfterReset,
|
|
1836
|
-
period: cadence ? formatDuration(cadence) : "month",
|
|
1837
|
-
|
|
1838
|
-
tierPrices: getTierPricesFromItem(item, currency, units)
|
|
1828
|
+
limit: suppressLimit ? void 0 : entitlement.issueAfterReset,
|
|
1829
|
+
period: suppressLimit ? void 0 : cadence ? formatDuration(cadence) : "month",
|
|
1830
|
+
tierPrices
|
|
1839
1831
|
});
|
|
1840
1832
|
continue;
|
|
1841
1833
|
}
|
|
@@ -1881,7 +1873,6 @@ const getPhaseRows = (opts) => {
|
|
|
1881
1873
|
entitlementType: f.entitlementType,
|
|
1882
1874
|
limit: f.entitlementType === "metered" ? f.limit : void 0,
|
|
1883
1875
|
period: f.entitlementType === "metered" ? f.period : void 0,
|
|
1884
|
-
overagePrice: f.entitlementType === "metered" ? f.overagePrice : void 0,
|
|
1885
1876
|
tierPrices: f.entitlementType === "metered" ? f.tierPrices : void 0,
|
|
1886
1877
|
value: f.entitlementType === "static" ? f.value : void 0,
|
|
1887
1878
|
phaseId: phase.id,
|
|
@@ -2001,18 +1992,10 @@ const SubscriptionPlanDetails = ({ subscription }) => {
|
|
|
2001
1992
|
children: [row.name, row.entitlementType === "static" && row.value !== void 0 ? `: ${row.value}` : ""]
|
|
2002
1993
|
}), /* @__PURE__ */ jsx("div", {
|
|
2003
1994
|
className: "text-muted-foreground",
|
|
2004
|
-
children: row.entitlementType === "metered" && row.limit != null ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
2005
|
-
|
|
2006
|
-
row.
|
|
2007
|
-
|
|
2008
|
-
className: "text-xs mt-1 space-y-0.5",
|
|
2009
|
-
children: row.tierPrices.map((line) => /* @__PURE__ */ jsx("li", { children: line }, line))
|
|
2010
|
-
}) : null,
|
|
2011
|
-
row.overagePrice ? /* @__PURE__ */ jsxs("div", {
|
|
2012
|
-
className: "text-xs mt-0.5",
|
|
2013
|
-
children: ["Overage: ", row.overagePrice]
|
|
2014
|
-
}) : null
|
|
2015
|
-
] }) : row.entitlementType === "static" && row.value !== void 0 ? null : "Included"
|
|
1995
|
+
children: row.entitlementType === "metered" && (row.limit != null || row.tierPrices && row.tierPrices.length > 0) ? /* @__PURE__ */ jsxs(Fragment$1, { children: [row.limit != null && (!row.tierPrices || row.tierPrices.length === 0) ? /* @__PURE__ */ jsxs(Fragment$1, { children: [formatNumber(row.limit), row.period ? ` / ${row.period}` : ""] }) : null, row.tierPrices && row.tierPrices.length > 0 ? /* @__PURE__ */ jsx("ul", {
|
|
1996
|
+
className: "text-xs space-y-0.5",
|
|
1997
|
+
children: row.tierPrices.map((line) => /* @__PURE__ */ jsx("li", { children: line }, line))
|
|
1998
|
+
}) : null] }) : row.entitlementType === "static" && row.value !== void 0 ? null : "Included"
|
|
2016
1999
|
})]
|
|
2017
2000
|
})
|
|
2018
2001
|
}, `${row.key}:${row.phaseId}`))
|
package/dist/pricing-ui.d.mts
CHANGED
|
@@ -84,8 +84,9 @@ interface Quota {
|
|
|
84
84
|
name: string;
|
|
85
85
|
limit: number;
|
|
86
86
|
period: string;
|
|
87
|
-
overagePrice?: string;
|
|
88
87
|
tierPrices?: string[];
|
|
88
|
+
isPayg?: boolean;
|
|
89
|
+
unitPrice?: string;
|
|
89
90
|
}
|
|
90
91
|
interface Feature {
|
|
91
92
|
key: string;
|
|
@@ -97,7 +98,7 @@ interface Alignment {
|
|
|
97
98
|
}
|
|
98
99
|
interface ProRatingConfig {
|
|
99
100
|
enabled: boolean;
|
|
100
|
-
mode: "
|
|
101
|
+
mode: "max_consumption_based";
|
|
101
102
|
}
|
|
102
103
|
interface ValidationError {
|
|
103
104
|
message: string;
|
|
@@ -253,7 +254,8 @@ declare const formatDurationInterval: (iso: string) => string;
|
|
|
253
254
|
* Returns an adjective form suitable for possessive context
|
|
254
255
|
* e.g. "your monthly quota", "your weekly limit".
|
|
255
256
|
* Falls back to "billing period" for multi-unit cadences
|
|
256
|
-
* where
|
|
257
|
+
* or sub-hour units where the adjective form is grammatically awkward
|
|
258
|
+
* (e.g. "every 3 months", "every 5 minutes").
|
|
257
259
|
*/
|
|
258
260
|
declare const formatDurationAdjective: (iso: string) => string;
|
|
259
261
|
//#endregion
|
|
@@ -276,7 +278,6 @@ declare const formatTieredPriceBreakdown: (opts: {
|
|
|
276
278
|
currency?: string;
|
|
277
279
|
unitLabel: string;
|
|
278
280
|
includedLabel: string;
|
|
279
|
-
omitIncludedUpToAmount?: number;
|
|
280
281
|
}) => string[] | undefined;
|
|
281
282
|
//#endregion
|
|
282
283
|
//#region src/utils/getPriceFromPlan.d.ts
|
package/dist/pricing-ui.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as formatDuration, a as subscriptionTaxLegendSentence, c as getPriceFromPlan, d as FeatureItem, f as categorizeRateCards, g as formatPrice, h as formatMinorCurrencyAmount, i as planHasDefaultTaxBehavior, l as PlanEntitlements, m as formatStaticEntitlementConfig, n as PricingCard, o as taxBehaviorLegendSentence, p as formatTieredPriceBreakdown, r as collectDefaultTaxBehaviors, s as derivePriceFromPlan, t as PricingTable, u as QuotaItem, v as formatDurationAdjective, y as formatDurationInterval } from "./PricingTable-
|
|
1
|
+
import { _ as formatDuration, a as subscriptionTaxLegendSentence, c as getPriceFromPlan, d as FeatureItem, f as categorizeRateCards, g as formatPrice, h as formatMinorCurrencyAmount, i as planHasDefaultTaxBehavior, l as PlanEntitlements, m as formatStaticEntitlementConfig, n as PricingCard, o as taxBehaviorLegendSentence, p as formatTieredPriceBreakdown, r as collectDefaultTaxBehaviors, s as derivePriceFromPlan, t as PricingTable, u as QuotaItem, v as formatDurationAdjective, y as formatDurationInterval } from "./PricingTable-DNop2iX9.mjs";
|
|
2
2
|
export { FeatureItem, PlanEntitlements, PricingCard, PricingTable, QuotaItem, categorizeRateCards, collectDefaultTaxBehaviors, derivePriceFromPlan, formatDuration, formatDurationAdjective, formatDurationInterval, formatMinorCurrencyAmount, formatPrice, formatStaticEntitlementConfig, formatTieredPriceBreakdown, getPriceFromPlan, planHasDefaultTaxBehavior, subscriptionTaxLegendSentence, taxBehaviorLegendSentence };
|
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.41",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/zuplo/zudoku",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"react": "19.2.5",
|
|
38
38
|
"react-dom": "19.2.5",
|
|
39
39
|
"tsdown": "0.22.0",
|
|
40
|
-
"zudoku": "0.
|
|
40
|
+
"zudoku": "0.79.1"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"react": ">=19.2.0",
|