@zuplo/zudoku-plugin-monetization 0.0.39 → 0.0.40

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 "every 3 months" would be grammatically awkward.
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, omitIncludedUpToAmount } = opts;
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 unitPart = unit > 0 ? `${formatPrice(unit, currency)}/${unitLabel}` : includedLabel;
117
- const flatPart = flat > 0 ? ` + ${formatPrice(flat, currency)} base` : "";
118
- const line = `${prefix}: ${unitPart}${flatPart}`;
119
- if (omitIncludedUpToAmount != null && upTo != null && upTo === omitIncludedUpToAmount && unitPart === includedLabel && flatPart === "") {} else lines.push(line);
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
- if (et.type === "metered" && et.issueAfterReset != null) {
134
- let overagePrice;
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
- const unitLabel = units?.[rc.key] ?? units?.[rc.featureKey ?? ""] ?? "unit";
138
- tierPrices = formatTieredPriceBreakdown({
139
- tiers: rc.price.tiers.map((t) => ({
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
- const overageTier = rc.price.tiers.find((t) => t.unitPrice?.amount && parseFloat(t.unitPrice.amount) > 0);
150
- if (et.isSoftLimit !== false && overageTier?.unitPrice) overagePrice = `${formatPrice(parseFloat(overageTier.unitPrice.amount), currency)}/${unitLabel}`;
151
- }
152
- quotas.push({
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("span", {
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: [quota.name, ":"]
319
+ children: quota.name
228
320
  }),
229
- " ",
230
- quota.limit.toLocaleString(),
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
- quota.overagePrice && /* @__PURE__ */ jsxs("div", {
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 price = getPriceFromPlan(plan);
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(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
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: isFree ? "Free" : formatPrice(price.monthly, plan.currency)
448
- }), !isFree && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
449
- className: "text-muted-foreground text-sm",
450
- children: ["/", billingInterval]
451
- }), showYearlyPrice && price.yearly > 0 && /* @__PURE__ */ jsxs("div", {
452
- className: "w-full text-sm text-muted-foreground mt-1",
453
- children: [formatPrice(price.yearly, plan.currency), "/year"]
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-Dsbd3E2Q.mjs";
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
- overagePrice: entitlement.isSoftLimit !== false ? getOveragePriceFromItem(item, currency, units) : void 0,
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
- formatNumber(row.limit),
2006
- row.period ? ` / ${row.period}` : "",
2007
- row.tierPrices && row.tierPrices.length > 0 ? /* @__PURE__ */ jsx("ul", {
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}`))
@@ -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;
@@ -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 "every 3 months" would be grammatically awkward.
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
@@ -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-Dsbd3E2Q.mjs";
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.39",
3
+ "version": "0.0.40",
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.78.1"
40
+ "zudoku": "0.79.0"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "react": ">=19.2.0",