@zuplo/zudoku-plugin-monetization 0.0.42 → 0.0.43

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.
@@ -265,6 +265,216 @@ const categorizeRateCards = (rateCards, options) => {
265
265
  };
266
266
  };
267
267
  //#endregion
268
+ //#region src/utils/comparePlanEntitlements.ts
269
+ /** Compact, human-readable value for a quota row. */
270
+ const quotaValueLabel = (q) => {
271
+ if (q.unitPrice) return q.unitPrice;
272
+ if (q.tierPrices && q.tierPrices.length > 0) return "Tiered pricing";
273
+ if (q.isPayg) return "Usage-based";
274
+ return `${q.limit.toLocaleString("en-US")} / ${q.period}`;
275
+ };
276
+ const featureValueLabel = (f) => f.value ?? "Included";
277
+ const isPlainNumericQuota = (q) => !q.isPayg && !q.unitPrice && (!q.tierPrices || q.tierPrices.length === 0);
278
+ const sameTierSchedule = (a, b) => (a ?? []).join("\n") === (b ?? []).join("\n");
279
+ const sameQuota = (a, b) => a.name === b.name && a.limit === b.limit && a.period === b.period && a.isPayg === b.isPayg && a.unitPrice === b.unitPrice && sameTierSchedule(a.tierPrices, b.tierPrices);
280
+ const sameFeature = (a, b) => a.name === b.name && a.value === b.value;
281
+ /**
282
+ * Whether two entitlement sets render identically: the same quota and feature
283
+ * keys, each with identical display fields. Order-insensitive (matched by
284
+ * key), so two phases whose rate cards merely differ in order still compare
285
+ * equal. Used to collapse per-phase entitlement lists that would repeat the
286
+ * exact same rows.
287
+ */
288
+ const sameEntitlementSet = (a, b) => {
289
+ if (a.quotas.length !== b.quotas.length || a.features.length !== b.features.length) return false;
290
+ const bQuotas = new Map(b.quotas.map((q) => [q.key, q]));
291
+ const bFeatures = new Map(b.features.map((f) => [f.key, f]));
292
+ return a.quotas.every((q) => {
293
+ const other = bQuotas.get(q.key);
294
+ return other !== void 0 && sameQuota(q, other);
295
+ }) && a.features.every((f) => {
296
+ const other = bFeatures.get(f.key);
297
+ return other !== void 0 && sameFeature(f, other);
298
+ });
299
+ };
300
+ /**
301
+ * Compare two plans' entitlements, matching strictly by feature key (never by
302
+ * display name). Each key yields exactly one change row, so a key that exists
303
+ * on one side and a differently-keyed feature that merely shares a display
304
+ * name can never read as a contradictory "added" + "removed" of the same
305
+ * thing. Labels are disambiguated afterwards when they would collide.
306
+ */
307
+ const comparePlanEntitlements = (current, target) => {
308
+ const changes = [];
309
+ const curQuota = new Map(current.quotas.map((q) => [q.key, q]));
310
+ const tgtQuota = new Map(target.quotas.map((q) => [q.key, q]));
311
+ const curFeat = new Map(current.features.map((f) => [f.key, f]));
312
+ const tgtFeat = new Map(target.features.map((f) => [f.key, f]));
313
+ for (const key of new Set([...curQuota.keys(), ...tgtQuota.keys()])) {
314
+ const c = curQuota.get(key);
315
+ const t = tgtQuota.get(key);
316
+ if (c && t) {
317
+ const currentValue = quotaValueLabel(c);
318
+ const targetValue = quotaValueLabel(t);
319
+ let change = "same";
320
+ if (isPlainNumericQuota(c) && isPlainNumericQuota(t) && c.period === t.period) {
321
+ if (t.limit > c.limit) change = "increase";
322
+ else if (t.limit < c.limit) change = "decrease";
323
+ } else if (currentValue !== targetValue || !sameTierSchedule(c.tierPrices, t.tierPrices)) change = "changed";
324
+ changes.push({
325
+ key,
326
+ label: t.name,
327
+ kind: "quota",
328
+ change,
329
+ currentValue,
330
+ targetValue,
331
+ tierPrices: t.tierPrices,
332
+ period: t.period
333
+ });
334
+ } else if (t) changes.push({
335
+ key,
336
+ label: t.name,
337
+ kind: "quota",
338
+ change: "added",
339
+ targetValue: quotaValueLabel(t),
340
+ tierPrices: t.tierPrices,
341
+ period: t.period
342
+ });
343
+ else if (c) changes.push({
344
+ key,
345
+ label: c.name,
346
+ kind: "quota",
347
+ change: "removed",
348
+ currentValue: quotaValueLabel(c),
349
+ period: c.period
350
+ });
351
+ }
352
+ for (const key of new Set([...curFeat.keys(), ...tgtFeat.keys()])) {
353
+ const c = curFeat.get(key);
354
+ const t = tgtFeat.get(key);
355
+ if (c && t) {
356
+ const currentValue = featureValueLabel(c);
357
+ const targetValue = featureValueLabel(t);
358
+ changes.push({
359
+ key,
360
+ label: t.name,
361
+ kind: "feature",
362
+ change: currentValue === targetValue ? "same" : "changed",
363
+ currentValue,
364
+ targetValue
365
+ });
366
+ } else if (t) changes.push({
367
+ key,
368
+ label: t.name,
369
+ kind: "feature",
370
+ change: "added",
371
+ targetValue: featureValueLabel(t)
372
+ });
373
+ else if (c) changes.push({
374
+ key,
375
+ label: c.name,
376
+ kind: "feature",
377
+ change: "removed",
378
+ currentValue: featureValueLabel(c)
379
+ });
380
+ }
381
+ const labelCounts = /* @__PURE__ */ new Map();
382
+ for (const ch of changes) labelCounts.set(ch.label, (labelCounts.get(ch.label) ?? 0) + 1);
383
+ return changes.map(({ period, ...ch }) => {
384
+ if ((labelCounts.get(ch.label) ?? 0) > 1 && ch.kind === "quota" && period) return {
385
+ ...ch,
386
+ label: `${ch.label} (${period})`
387
+ };
388
+ return ch;
389
+ });
390
+ };
391
+ //#endregion
392
+ //#region src/utils/getPlanPrice.ts
393
+ const sumFlatFeeAmounts = (rateCards) => {
394
+ let total = 0;
395
+ for (const rc of rateCards) if (rc.type === "flat_fee" && rc.price && rc.billingCadence !== null) {
396
+ const amount = Number(rc.price.amount);
397
+ if (Number.isFinite(amount)) total += amount;
398
+ }
399
+ return total;
400
+ };
401
+ /**
402
+ * The plan's headline recurring price: the sum of every recurring `flat_fee`
403
+ * rate-card amount on the plan's steady-state (last) phase, expressed in the
404
+ * plan's own `billingCadence`. One-time fees (`flat_fee` with
405
+ * `billingCadence: null`, e.g. a setup fee) are excluded.
406
+ *
407
+ * This is derived entirely from the plan's rate cards. It deliberately does
408
+ * NOT read any server-provided `monthlyPrice` / `yearlyPrice` and performs no
409
+ * cadence conversion, so it stays correct for any billing cadence — hourly
410
+ * (`PT1H`), weekly, monthly, yearly, etc. Callers pair the returned amount
411
+ * with `formatDuration(plan.billingCadence)` to render e.g. `$2.99/hour`.
412
+ *
413
+ * Returns `0` when there are no phases or no recurring flat fee, which callers
414
+ * render as "Free" (or "Pay as you go" when the plan bills on usage — see
415
+ * {@link formatPlanPrice}).
416
+ */
417
+ const getPlanPrice = (plan) => {
418
+ const lastPhase = plan.phases?.at(-1);
419
+ if (!lastPhase) return 0;
420
+ return sumFlatFeeAmounts(lastPhase.rateCards ?? []);
421
+ };
422
+ //#endregion
423
+ //#region src/utils/formatPlanPrice.ts
424
+ const isPricedUsageRateCard = (rc) => {
425
+ if (rc.type !== "usage_based" || !rc.price) return false;
426
+ const p = rc.price;
427
+ if (p.type === "unit") return parseFloat(p.amount) > 0;
428
+ if (p.type === "tiered") return p.tiers.some(tierHasPositivePrice);
429
+ return true;
430
+ };
431
+ const hasPricedUsageRateCard = (plan) => plan.phases.some((phase) => phase.rateCards.some(isPricedUsageRateCard));
432
+ /**
433
+ * Headline pricing for plan cards. Centralizes the "Pay as you go" detection:
434
+ * plans whose flat-fee total is zero but that bill on usage shouldn't render
435
+ * as "Free" - they're charged per-unit.
436
+ */
437
+ const formatPlanPrice = (plan) => {
438
+ if (!plan.phases || plan.phases.length === 0) return { type: "free" };
439
+ const amount = getPlanPrice(plan);
440
+ if (amount > 0) return {
441
+ type: "priced",
442
+ amount
443
+ };
444
+ if (hasPricedUsageRateCard(plan)) return {
445
+ type: "payg",
446
+ main: "Pay as you go",
447
+ sub: "Usage-based pricing"
448
+ };
449
+ return { type: "free" };
450
+ };
451
+ //#endregion
452
+ //#region src/utils/getPhasePriceLabel.ts
453
+ /**
454
+ * Headline price for a SINGLE phase, derived only from that phase's own rate
455
+ * cards. Mirrors {@link formatPlanPrice}'s rules, but scoped to the phase:
456
+ * a positive recurring flat-fee total is `priced`; otherwise a priced
457
+ * `usage_based` card in this phase makes it `payg`; otherwise it's `free`.
458
+ *
459
+ * Like {@link getPlanPrice}, one-time fees (`flat_fee` with
460
+ * `billingCadence: null`) and `price: null` rate cards contribute nothing —
461
+ * an intro phase whose fees all have `price: null` derives as `free`.
462
+ */
463
+ const getPhasePriceLabel = (phase) => {
464
+ const rateCards = phase.rateCards ?? [];
465
+ const amount = sumFlatFeeAmounts(rateCards);
466
+ if (amount > 0) return {
467
+ type: "priced",
468
+ amount
469
+ };
470
+ if (rateCards.some(isPricedUsageRateCard)) return {
471
+ type: "payg",
472
+ main: "Pay as you go",
473
+ sub: "Usage-based pricing"
474
+ };
475
+ return { type: "free" };
476
+ };
477
+ //#endregion
268
478
  //#region src/pricing-ui/CheckIcon.tsx
269
479
  /**
270
480
  * Inline `Check` icon, visually identical to `lucide-react`'s `CheckIcon`
@@ -372,43 +582,128 @@ const EntitlementList = ({ quotas, features, header, itemClassName }) => {
372
582
  };
373
583
  //#endregion
374
584
  //#region src/pricing-ui/PlanEntitlements.tsx
375
- const PhaseSection = ({ phase, currency, showName, billingCadence, units, itemClassName }) => {
376
- const { quotas, features } = categorizeRateCards(phase.rateCards, {
585
+ const priceLabelText = (label, currency, billingCadence) => {
586
+ if (label.type === "payg") return label.main;
587
+ if (label.type === "free") return "Free";
588
+ const amount = formatPrice(label.amount, currency);
589
+ return billingCadence ? `${amount}/${formatDuration(billingCadence)}` : amount;
590
+ };
591
+ /**
592
+ * Section header for one phase of a multi-phase plan: the phase name, its
593
+ * duration, and the phase's own price. Shared by {@link PlanEntitlements} and
594
+ * the plan-change card so per-phase sections read identically everywhere.
595
+ */
596
+ const PlanPhaseHeader = ({ phase, currency, billingCadence }) => /* @__PURE__ */ jsxs("div", {
597
+ className: "text-sm font-medium text-card-foreground",
598
+ children: [
599
+ phase.name,
600
+ phase.duration && /* @__PURE__ */ jsxs("span", {
601
+ className: "text-muted-foreground font-normal",
602
+ children: [
603
+ " ",
604
+ "— ",
605
+ formatDuration(phase.duration)
606
+ ]
607
+ }),
608
+ /* @__PURE__ */ jsxs("span", {
609
+ className: "text-muted-foreground font-normal",
610
+ children: [
611
+ " ",
612
+ "·",
613
+ " ",
614
+ priceLabelText(getPhasePriceLabel(phase), currency, billingCadence)
615
+ ]
616
+ })
617
+ ]
618
+ });
619
+ const PhaseSection = ({ phase, set, currency, billingCadence, itemClassName }) => /* @__PURE__ */ jsx(EntitlementList, {
620
+ quotas: set.quotas,
621
+ features: set.features,
622
+ itemClassName,
623
+ header: /* @__PURE__ */ jsx(PlanPhaseHeader, {
624
+ phase,
625
+ currency,
626
+ billingCadence
627
+ })
628
+ });
629
+ /**
630
+ * A plan's entitlements, phase by phase. Multi-phase plans whose phases all
631
+ * resolve to the same entitlements collapse into a single list (the phases
632
+ * only differ in price, which the price schedule already tells); phases with
633
+ * genuinely different entitlements render as separate sections headed by the
634
+ * phase name, duration, and that phase's own price.
635
+ */
636
+ const PlanEntitlements = ({ phases, currency, billingCadence, units, itemClassName }) => {
637
+ const sets = phases.map((phase) => categorizeRateCards(phase.rateCards, {
377
638
  currency,
378
639
  units,
379
640
  planBillingCadence: billingCadence
380
- });
381
- return /* @__PURE__ */ jsx(EntitlementList, {
382
- quotas,
383
- features,
384
- itemClassName,
385
- header: showName ? /* @__PURE__ */ jsxs("div", {
386
- className: "text-sm font-medium text-card-foreground",
387
- children: [phase.name, phase.duration && /* @__PURE__ */ jsxs("span", {
388
- className: "text-muted-foreground font-normal",
389
- children: [
390
- " ",
391
- "— ",
392
- formatDuration(phase.duration)
393
- ]
394
- })]
395
- }) : void 0
396
- });
397
- };
398
- const PlanEntitlements = ({ phases, currency, billingCadence, units, itemClassName }) => {
641
+ }));
642
+ if (phases.length <= 1 || sets.every((set) => sameEntitlementSet(set, sets[0]))) {
643
+ const steady = sets.at(-1);
644
+ return /* @__PURE__ */ jsx("div", {
645
+ className: "space-y-4",
646
+ children: steady && /* @__PURE__ */ jsx(EntitlementList, {
647
+ quotas: steady.quotas,
648
+ features: steady.features,
649
+ itemClassName
650
+ })
651
+ });
652
+ }
399
653
  return /* @__PURE__ */ jsx("div", {
400
654
  className: "space-y-4",
401
655
  children: phases.map((phase, idx) => /* @__PURE__ */ jsx(PhaseSection, {
402
656
  phase,
657
+ set: sets[idx],
403
658
  currency,
404
- showName: phases.length > 1,
405
659
  billingCadence,
406
- units,
407
660
  itemClassName
408
661
  }, phase.key ?? String(idx)))
409
662
  });
410
663
  };
411
664
  //#endregion
665
+ //#region src/pricing-ui/PlanPriceSchedule.tsx
666
+ const RowPrice = ({ price, currency, billingCadence, className }) => {
667
+ if (price.type === "priced") return /* @__PURE__ */ jsxs("span", {
668
+ className: cn("font-semibold text-card-foreground", className),
669
+ children: [formatPrice(price.amount, currency), billingCadence && /* @__PURE__ */ jsxs("span", {
670
+ className: "text-muted-foreground font-normal text-sm",
671
+ children: ["/", formatDuration(billingCadence)]
672
+ })]
673
+ });
674
+ return /* @__PURE__ */ jsx("span", {
675
+ className: cn("font-semibold text-card-foreground", className),
676
+ children: price.type === "payg" ? price.main : "Free"
677
+ });
678
+ };
679
+ /**
680
+ * Stacked per-phase price rows for a multi-phase plan, replacing the single
681
+ * headline price (which only reflects the steady-state phase): each row pairs
682
+ * a phase label ("First 3 months", "After that") with that phase's own price.
683
+ * Every row gets equal visual weight — the intro price is part of the plan's
684
+ * price, not a footnote.
685
+ *
686
+ * Callers derive the rows via {@link getPlanPriceSchedule} and fall back to
687
+ * the single-price rendering when it returns `undefined`. `size` picks the
688
+ * typographic treatment: `"lg"` for a card's headline area, `"sm"` for
689
+ * compact contexts (plan-change rows, summary cards).
690
+ */
691
+ const PlanPriceSchedule = ({ schedule, currency, billingCadence, size = "sm", className }) => /* @__PURE__ */ jsx("div", {
692
+ className: cn("space-y-1 text-sm", className),
693
+ children: schedule.map((row) => /* @__PURE__ */ jsxs("div", {
694
+ className: "flex items-baseline justify-between gap-3",
695
+ children: [/* @__PURE__ */ jsx("span", {
696
+ className: "text-muted-foreground",
697
+ children: row.label
698
+ }), /* @__PURE__ */ jsx(RowPrice, {
699
+ price: row.price,
700
+ currency,
701
+ billingCadence,
702
+ className: size === "lg" ? "text-lg" : void 0
703
+ })]
704
+ }, row.key))
705
+ });
706
+ //#endregion
412
707
  //#region src/pricing-ui/PlanPriceTag.tsx
413
708
  /**
414
709
  * Headline price for a plan/subscription: `$X/cadence`, "Pay as you go", or
@@ -445,64 +740,38 @@ const PlanPriceTag = ({ label, currency, billingCadence, description = false, si
445
740
  });
446
741
  };
447
742
  //#endregion
448
- //#region src/utils/getPlanPrice.ts
449
- const sumFlatFeeAmounts = (rateCards) => {
450
- let total = 0;
451
- for (const rc of rateCards) if (rc.type === "flat_fee" && rc.price && rc.billingCadence !== null) {
452
- const amount = Number(rc.price.amount);
453
- if (Number.isFinite(amount)) total += amount;
454
- }
455
- return total;
743
+ //#region src/utils/getPlanPriceSchedule.ts
744
+ const samePriceLabel = (a, b) => {
745
+ if (a.type !== b.type) return false;
746
+ return a.type !== "priced" || b.type !== "priced" || a.amount === b.amount;
456
747
  };
457
- /**
458
- * The plan's headline recurring price: the sum of every recurring `flat_fee`
459
- * rate-card amount on the plan's steady-state (last) phase, expressed in the
460
- * plan's own `billingCadence`. One-time fees (`flat_fee` with
461
- * `billingCadence: null`, e.g. a setup fee) are excluded.
462
- *
463
- * This is derived entirely from the plan's rate cards. It deliberately does
464
- * NOT read any server-provided `monthlyPrice` / `yearlyPrice` and performs no
465
- * cadence conversion, so it stays correct for any billing cadence — hourly
466
- * (`PT1H`), weekly, monthly, yearly, etc. Callers pair the returned amount
467
- * with `formatDuration(plan.billingCadence)` to render e.g. `$2.99/hour`.
468
- *
469
- * Returns `0` when there are no phases or no recurring flat fee, which callers
470
- * render as "Free" (or "Pay as you go" when the plan bills on usage — see
471
- * {@link formatPlanPrice}).
472
- */
473
- const getPlanPrice = (plan) => {
474
- const lastPhase = plan.phases?.at(-1);
475
- if (!lastPhase) return 0;
476
- return sumFlatFeeAmounts(lastPhase.rateCards ?? []);
748
+ const rowLabel = (phase, index, lastIndex) => {
749
+ if (index === lastIndex) return "After that";
750
+ if (!phase.duration) return phase.name;
751
+ const duration = formatDuration(phase.duration);
752
+ return index === 0 ? `First ${duration}` : `Next ${duration}`;
477
753
  };
478
- //#endregion
479
- //#region src/utils/formatPlanPrice.ts
480
- const isPricedUsageRateCard = (rc) => {
481
- if (rc.type !== "usage_based" || !rc.price) return false;
482
- const p = rc.price;
483
- if (p.type === "unit") return parseFloat(p.amount) > 0;
484
- if (p.type === "tiered") return p.tiers.some(tierHasPositivePrice);
485
- return true;
486
- };
487
- const hasPricedUsageRateCard = (plan) => plan.phases.some((phase) => phase.rateCards.some(isPricedUsageRateCard));
488
754
  /**
489
- * Headline pricing for plan cards. Centralizes the "Pay as you go" detection:
490
- * plans whose flat-fee total is zero but that bill on usage shouldn't render
491
- * as "Free" - they're charged per-unit.
755
+ * A stacked price schedule for a multi-phase plan: one row per phase, each
756
+ * priced from its own rate cards (e.g. "First 3 months Free" then
757
+ * "After that — $750/month"). This is how an intro/ramp phase's price gets
758
+ * surfaced instead of only the steady-state price from {@link getPlanPrice}.
759
+ *
760
+ * Returns `undefined` when there is nothing to stack — fewer than two phases,
761
+ * or every phase resolving to the same price label (a free trial into a free
762
+ * plan, two identically-priced phases, …) — so callers fall back to the
763
+ * single-headline rendering.
492
764
  */
493
- const formatPlanPrice = (plan) => {
494
- if (!plan.phases || plan.phases.length === 0) return { type: "free" };
495
- const amount = getPlanPrice(plan);
496
- if (amount > 0) return {
497
- type: "priced",
498
- amount
499
- };
500
- if (hasPricedUsageRateCard(plan)) return {
501
- type: "payg",
502
- main: "Pay as you go",
503
- sub: "Usage-based pricing"
504
- };
505
- return { type: "free" };
765
+ const getPlanPriceSchedule = (plan) => {
766
+ const phases = plan.phases ?? [];
767
+ if (phases.length <= 1) return void 0;
768
+ const prices = phases.map(getPhasePriceLabel);
769
+ if (prices.every((price) => samePriceLabel(price, prices[0]))) return;
770
+ return phases.map((phase, index) => ({
771
+ key: phase.key ?? String(index),
772
+ label: rowLabel(phase, index, phases.length - 1),
773
+ price: prices[index]
774
+ }));
506
775
  };
507
776
  //#endregion
508
777
  //#region src/utils/pricingTaxLegend.ts
@@ -557,6 +826,7 @@ const PricingCard = ({ plan, isPopular = false, units, action, className }) => {
557
826
  const priceLabel = formatPlanPrice(plan);
558
827
  const isCustom = isCustomPlan(plan);
559
828
  const billingInterval = formatDuration(plan.billingCadence);
829
+ const schedule = isCustom ? void 0 : getPlanPriceSchedule(plan);
560
830
  return /* @__PURE__ */ jsxs("div", {
561
831
  className: cn("relative rounded-lg border p-6 flex flex-col", isPopular && "border-primary border-2", className),
562
832
  children: [
@@ -574,7 +844,12 @@ const PricingCard = ({ plan, isPopular = false, units, action, className }) => {
574
844
  className: "text-base font-semibold text-muted-foreground mb-2",
575
845
  children: plan.name
576
846
  }),
577
- /* @__PURE__ */ jsx("div", {
847
+ schedule ? /* @__PURE__ */ jsx(PlanPriceSchedule, {
848
+ schedule,
849
+ currency: plan.currency,
850
+ billingCadence: plan.billingCadence,
851
+ size: "lg"
852
+ }) : /* @__PURE__ */ jsx("div", {
578
853
  className: "flex items-baseline gap-1 flex-wrap",
579
854
  children: isCustom ? /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
580
855
  className: "text-3xl font-bold text-card-foreground",
@@ -660,4 +935,4 @@ const PricingTable = ({ plans, units, renderAction, renderCard, isPopular = (pla
660
935
  })] });
661
936
  };
662
937
  //#endregion
663
- export { formatDurationInterval as S, formatStaticEntitlementConfig as _, planHasDefaultTaxBehavior as a, formatDuration as b, formatPlanPrice as c, PlanEntitlements as d, EntitlementList as f, formatTieredPriceBreakdown as g, categorizeRateCards as h, collectDefaultTaxBehaviors as i, getPlanPrice as l, FeatureItem as m, PricingCard as n, subscriptionTaxLegendSentence as o, QuotaItem as p, isCustomPlan as r, taxBehaviorLegendSentence as s, PricingTable as t, PlanPriceTag as u, formatMinorCurrencyAmount as v, formatDurationAdjective as x, formatPrice as y };
938
+ export { formatStaticEntitlementConfig as C, formatDurationAdjective as D, formatDuration as E, formatDurationInterval as O, formatTieredPriceBreakdown as S, formatPrice as T, formatPlanPrice as _, planHasDefaultTaxBehavior as a, sameEntitlementSet as b, getPlanPriceSchedule as c, PlanEntitlements as d, PlanPhaseHeader as f, getPhasePriceLabel as g, FeatureItem as h, collectDefaultTaxBehaviors as i, PlanPriceTag as l, QuotaItem as m, PricingCard as n, subscriptionTaxLegendSentence as o, EntitlementList as p, isCustomPlan as r, taxBehaviorLegendSentence as s, PricingTable as t, PlanPriceSchedule as u, getPlanPrice as v, formatMinorCurrencyAmount as w, categorizeRateCards as x, comparePlanEntitlements as y };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { S as formatDurationInterval, a as planHasDefaultTaxBehavior, b as formatDuration, c as formatPlanPrice, d as PlanEntitlements, f as EntitlementList, h as categorizeRateCards, o as subscriptionTaxLegendSentence, r as isCustomPlan, t as PricingTable, u as PlanPriceTag, v as formatMinorCurrencyAmount, x as formatDurationAdjective, y as formatPrice } from "./PricingTable-BlcXx4-5.mjs";
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-WkG2n7V-.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 { Link, Outlet, useLocation, useNavigate, useSearchParams } from "zudoku/router";
@@ -256,7 +256,9 @@ const formatBillingCycle = (duration) => {
256
256
  * Plan summary shown on the checkout and plan-change confirmation pages: an
257
257
  * avatar + name/description on the left and the headline price (or
258
258
  * "Free" / "Pay as you go") plus tax and billing cadence on the right,
259
- * followed by the plan's included entitlements.
259
+ * followed by the plan's included entitlements. Multi-phase ramp plans render
260
+ * a full-width per-phase price schedule beneath the title instead of the
261
+ * single right-column price.
260
262
  *
261
263
  * The price is derived from the plan's rate cards via {@link formatPlanPrice}
262
264
  * and rendered in the plan's own billing cadence, so it stays correct for any
@@ -264,10 +266,19 @@ const formatBillingCycle = (duration) => {
264
266
  */
265
267
  const PlanSummaryCard = ({ plan, descriptionFallback, taxAmount, taxLabel, taxInclusive, units, entitlementsItemClassName }) => {
266
268
  const priceLabel = formatPlanPrice(plan);
269
+ const schedule = getPlanPriceSchedule(plan);
267
270
  const billingCycle = plan.billingCadence ? formatDuration(plan.billingCadence) : null;
271
+ const taxLine = taxAmount != null && /* @__PURE__ */ jsx("div", {
272
+ className: "text-sm font-normal mt-1",
273
+ children: taxInclusive ? `${formatMinorCurrencyAmount(taxAmount, plan.currency)} ${taxLabel} included` : `+ ${formatMinorCurrencyAmount(taxAmount, plan.currency)} ${taxLabel}`
274
+ });
275
+ const billedLine = billingCycle && /* @__PURE__ */ jsxs("div", {
276
+ className: "text-sm text-muted-foreground font-normal",
277
+ children: ["Billed ", formatBillingCycle(billingCycle)]
278
+ });
268
279
  return /* @__PURE__ */ jsxs(Card, {
269
280
  className: "bg-muted/50",
270
- children: [/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, {
281
+ children: [/* @__PURE__ */ jsxs(CardHeader, { children: [/* @__PURE__ */ jsxs(CardTitle, {
271
282
  className: "flex justify-between items-start",
272
283
  children: [/* @__PURE__ */ jsxs("div", {
273
284
  className: "flex items-center gap-3",
@@ -284,22 +295,26 @@ const PlanSummaryCard = ({ plan, descriptionFallback, taxAmount, taxLabel, taxIn
284
295
  children: plan.description || descriptionFallback
285
296
  })]
286
297
  })]
287
- }), /* @__PURE__ */ jsxs("div", {
298
+ }), !schedule && /* @__PURE__ */ jsxs("div", {
288
299
  className: "text-right",
289
300
  children: [/* @__PURE__ */ jsx(PlanPriceTag, {
290
301
  label: priceLabel,
291
302
  currency: plan.currency,
292
303
  size: "lg",
293
304
  description: true
294
- }), priceLabel.type === "priced" && /* @__PURE__ */ jsxs(Fragment$1, { children: [taxAmount != null && /* @__PURE__ */ jsx("div", {
295
- className: "text-sm font-normal mt-1",
296
- children: taxInclusive ? `${formatMinorCurrencyAmount(taxAmount, plan.currency)} ${taxLabel} included` : `+ ${formatMinorCurrencyAmount(taxAmount, plan.currency)} ${taxLabel}`
297
- }), billingCycle && /* @__PURE__ */ jsxs("div", {
298
- className: "text-sm text-muted-foreground font-normal",
299
- children: ["Billed ", formatBillingCycle(billingCycle)]
300
- })] })]
305
+ }), priceLabel.type === "priced" && /* @__PURE__ */ jsxs(Fragment$1, { children: [taxLine, billedLine] })]
306
+ })]
307
+ }), schedule && /* @__PURE__ */ jsxs("div", {
308
+ className: "mt-3 font-normal",
309
+ children: [/* @__PURE__ */ jsx(PlanPriceSchedule, {
310
+ schedule,
311
+ currency: plan.currency,
312
+ billingCadence: plan.billingCadence
313
+ }), /* @__PURE__ */ jsxs("div", {
314
+ className: "text-right",
315
+ children: [taxLine, billedLine]
301
316
  })]
302
- }) }), /* @__PURE__ */ jsxs(CardContent, { children: [
317
+ })] }), /* @__PURE__ */ jsxs(CardContent, { children: [
303
318
  /* @__PURE__ */ jsx(Separator, {}),
304
319
  /* @__PURE__ */ jsx("div", {
305
320
  className: "text-sm font-medium mb-3 mt-3",
@@ -1431,130 +1446,6 @@ const RestoreSubscriptionDialog = ({ open, onOpenChange, planName, subscriptionI
1431
1446
  });
1432
1447
  };
1433
1448
  //#endregion
1434
- //#region src/utils/comparePlanEntitlements.ts
1435
- /** Compact, human-readable value for a quota row. */
1436
- const quotaValueLabel = (q) => {
1437
- if (q.unitPrice) return q.unitPrice;
1438
- if (q.tierPrices && q.tierPrices.length > 0) return "Tiered pricing";
1439
- if (q.isPayg) return "Usage-based";
1440
- return `${q.limit.toLocaleString("en-US")} / ${q.period}`;
1441
- };
1442
- const featureValueLabel = (f) => f.value ?? "Included";
1443
- const isPlainNumericQuota = (q) => !q.isPayg && !q.unitPrice && (!q.tierPrices || q.tierPrices.length === 0);
1444
- const sameTierSchedule = (a, b) => (a ?? []).join("\n") === (b ?? []).join("\n");
1445
- /**
1446
- * Compare two plans' entitlements, matching strictly by feature key (never by
1447
- * display name). Each key yields exactly one change row, so a key that exists
1448
- * on one side and a differently-keyed feature that merely shares a display
1449
- * name can never read as a contradictory "added" + "removed" of the same
1450
- * thing. Labels are disambiguated afterwards when they would collide.
1451
- */
1452
- const comparePlanEntitlements = (current, target) => {
1453
- const changes = [];
1454
- const curQuota = new Map(current.quotas.map((q) => [q.key, q]));
1455
- const tgtQuota = new Map(target.quotas.map((q) => [q.key, q]));
1456
- const curFeat = new Map(current.features.map((f) => [f.key, f]));
1457
- const tgtFeat = new Map(target.features.map((f) => [f.key, f]));
1458
- for (const key of new Set([...curQuota.keys(), ...tgtQuota.keys()])) {
1459
- const c = curQuota.get(key);
1460
- const t = tgtQuota.get(key);
1461
- if (c && t) {
1462
- const currentValue = quotaValueLabel(c);
1463
- const targetValue = quotaValueLabel(t);
1464
- let change = "same";
1465
- if (isPlainNumericQuota(c) && isPlainNumericQuota(t) && c.period === t.period) {
1466
- if (t.limit > c.limit) change = "increase";
1467
- else if (t.limit < c.limit) change = "decrease";
1468
- } else if (currentValue !== targetValue || !sameTierSchedule(c.tierPrices, t.tierPrices)) change = "changed";
1469
- changes.push({
1470
- key,
1471
- label: t.name,
1472
- kind: "quota",
1473
- change,
1474
- currentValue,
1475
- targetValue,
1476
- tierPrices: t.tierPrices,
1477
- period: t.period
1478
- });
1479
- } else if (t) changes.push({
1480
- key,
1481
- label: t.name,
1482
- kind: "quota",
1483
- change: "added",
1484
- targetValue: quotaValueLabel(t),
1485
- tierPrices: t.tierPrices,
1486
- period: t.period
1487
- });
1488
- else if (c) changes.push({
1489
- key,
1490
- label: c.name,
1491
- kind: "quota",
1492
- change: "removed",
1493
- currentValue: quotaValueLabel(c),
1494
- period: c.period
1495
- });
1496
- }
1497
- for (const key of new Set([...curFeat.keys(), ...tgtFeat.keys()])) {
1498
- const c = curFeat.get(key);
1499
- const t = tgtFeat.get(key);
1500
- if (c && t) {
1501
- const currentValue = featureValueLabel(c);
1502
- const targetValue = featureValueLabel(t);
1503
- changes.push({
1504
- key,
1505
- label: t.name,
1506
- kind: "feature",
1507
- change: currentValue === targetValue ? "same" : "changed",
1508
- currentValue,
1509
- targetValue
1510
- });
1511
- } else if (t) changes.push({
1512
- key,
1513
- label: t.name,
1514
- kind: "feature",
1515
- change: "added",
1516
- targetValue: featureValueLabel(t)
1517
- });
1518
- else if (c) changes.push({
1519
- key,
1520
- label: c.name,
1521
- kind: "feature",
1522
- change: "removed",
1523
- currentValue: featureValueLabel(c)
1524
- });
1525
- }
1526
- const labelCounts = /* @__PURE__ */ new Map();
1527
- for (const ch of changes) labelCounts.set(ch.label, (labelCounts.get(ch.label) ?? 0) + 1);
1528
- return changes.map(({ period, ...ch }) => {
1529
- if ((labelCounts.get(ch.label) ?? 0) > 1 && ch.kind === "quota" && period) return {
1530
- ...ch,
1531
- label: `${ch.label} (${period})`
1532
- };
1533
- return ch;
1534
- });
1535
- };
1536
- //#endregion
1537
- //#region src/utils/formatPhaseRampSummary.ts
1538
- const durationWithCount = (iso) => {
1539
- const text = formatDuration(iso);
1540
- return /^\d/.test(text) ? text : `1 ${text}`;
1541
- };
1542
- const steadyStateLabel = (plan) => {
1543
- const label = formatPlanPrice(plan);
1544
- if (label.type === "priced") return `${formatPrice(label.amount, plan.currency)} / ${formatDuration(plan.billingCadence)}`;
1545
- return label.type === "payg" ? "Pay as you go" : "Free";
1546
- };
1547
- /**
1548
- * One-line summary of a multi-phase plan's progression for compact UI, e.g.
1549
- * `"Free Trial (1 week), then $2.99 / month"`. Returns `undefined` for
1550
- * single-phase plans (nothing to summarize).
1551
- */
1552
- const formatPhaseRampSummary = (plan) => {
1553
- if (!plan.phases || plan.phases.length <= 1) return void 0;
1554
- const first = plan.phases[0];
1555
- return `${first.duration ? `${first.name} (${durationWithCount(first.duration)})` : first.name}, then ${steadyStateLabel(plan)}`;
1556
- };
1557
- //#endregion
1558
1449
  //#region src/pages/components/PlanChangeCard.tsx
1559
1450
  const MODE_LABEL = {
1560
1451
  upgrade: "Upgrade",
@@ -1665,18 +1556,28 @@ const ChangeRow = ({ change }) => {
1665
1556
  const PlanChangeCard = ({ plan, mode, currentEntitlements, isNewerVersion, isSwitching, units, onSwitch }) => {
1666
1557
  const isCustom = isCustomPlan(plan);
1667
1558
  const priceLabel = formatPlanPrice(plan);
1668
- const ramp = formatPhaseRampSummary(plan);
1669
- const entitlementChanges = useMemo(() => {
1670
- const steadyPhase = plan.phases.at(-1);
1671
- const changes = comparePlanEntitlements(currentEntitlements, steadyPhase ? categorizeRateCards(steadyPhase.rateCards, {
1559
+ const schedule = isCustom ? void 0 : getPlanPriceSchedule(plan);
1560
+ const phaseChangeGroups = useMemo(() => {
1561
+ const diff = (target) => {
1562
+ const changes = comparePlanEntitlements(currentEntitlements, target);
1563
+ return [...changes.filter((c) => c.change !== "removed"), ...changes.filter((c) => c.change === "removed")];
1564
+ };
1565
+ const sets = plan.phases.map((phase) => categorizeRateCards(phase.rateCards, {
1672
1566
  currency: plan.currency,
1673
1567
  units,
1674
1568
  planBillingCadence: plan.billingCadence
1675
- }) : {
1569
+ }));
1570
+ return (plan.phases.length <= 1 || sets.every((set) => sameEntitlementSet(set, sets[0])) ? [{ changes: diff(sets.at(-1) ?? {
1676
1571
  quotas: [],
1677
1572
  features: []
1678
- });
1679
- return [...changes.filter((c) => c.change !== "removed"), ...changes.filter((c) => c.change === "removed")];
1573
+ }) }] : plan.phases.flatMap((phase, idx) => {
1574
+ const set = sets[idx];
1575
+ if (set.quotas.length === 0 && set.features.length === 0) return [];
1576
+ return [{
1577
+ phase,
1578
+ changes: diff(set)
1579
+ }];
1580
+ })).filter((group) => group.changes.length > 0);
1680
1581
  }, [
1681
1582
  plan,
1682
1583
  currentEntitlements,
@@ -1702,7 +1603,7 @@ const PlanChangeCard = ({ plan, mode, currentEntitlements, isNewerVersion, isSwi
1702
1603
  }), isCustom ? /* @__PURE__ */ jsx("span", {
1703
1604
  className: "text-primary font-medium",
1704
1605
  children: "Custom"
1705
- }) : /* @__PURE__ */ jsx(PlanPriceTag, {
1606
+ }) : !schedule && /* @__PURE__ */ jsx(PlanPriceTag, {
1706
1607
  label: priceLabel,
1707
1608
  currency: plan.currency,
1708
1609
  billingCadence: plan.billingCadence
@@ -1719,13 +1620,22 @@ const PlanChangeCard = ({ plan, mode, currentEntitlements, isNewerVersion, isSwi
1719
1620
  children: MODE_LABEL[mode]
1720
1621
  })]
1721
1622
  }),
1722
- ramp && /* @__PURE__ */ jsx("p", {
1723
- className: "text-sm text-muted-foreground mb-2",
1724
- children: ramp
1623
+ schedule && /* @__PURE__ */ jsx(PlanPriceSchedule, {
1624
+ schedule,
1625
+ currency: plan.currency,
1626
+ billingCadence: plan.billingCadence,
1627
+ className: "mb-2"
1725
1628
  }),
1726
- entitlementChanges.length > 0 && /* @__PURE__ */ jsx("div", {
1727
- className: "space-y-1.5",
1728
- children: entitlementChanges.map((change) => /* @__PURE__ */ jsx(ChangeRow, { change }, `${change.kind}:${change.key}`))
1629
+ phaseChangeGroups.length > 0 && /* @__PURE__ */ jsx("div", {
1630
+ className: "space-y-3",
1631
+ children: phaseChangeGroups.map((group, idx) => /* @__PURE__ */ jsxs("div", {
1632
+ className: "space-y-1.5",
1633
+ children: [group.phase && /* @__PURE__ */ jsx(PlanPhaseHeader, {
1634
+ phase: group.phase,
1635
+ currency: plan.currency,
1636
+ billingCadence: plan.billingCadence
1637
+ }), group.changes.map((change) => /* @__PURE__ */ jsx(ChangeRow, { change }, `${change.kind}:${change.key}`))]
1638
+ }, group.phase?.key ?? String(idx)))
1729
1639
  })
1730
1640
  ]
1731
1641
  });
@@ -142,6 +142,13 @@ declare const FeatureItem: ({
142
142
  }) => import("react/jsx-runtime").JSX.Element;
143
143
  //#endregion
144
144
  //#region src/pricing-ui/PlanEntitlements.d.ts
145
+ /**
146
+ * A plan's entitlements, phase by phase. Multi-phase plans whose phases all
147
+ * resolve to the same entitlements collapse into a single list (the phases
148
+ * only differ in price, which the price schedule already tells); phases with
149
+ * genuinely different entitlements render as separate sections headed by the
150
+ * phase name, duration, and that phase's own price.
151
+ */
145
152
  declare const PlanEntitlements: ({
146
153
  phases,
147
154
  currency,
@@ -174,6 +181,52 @@ type PlanPriceLabel = {
174
181
  */
175
182
  declare const formatPlanPrice: (plan: Plan) => PlanPriceLabel;
176
183
  //#endregion
184
+ //#region src/utils/getPlanPriceSchedule.d.ts
185
+ type PlanPriceScheduleRow = {
186
+ /** Stable row key — the phase key, falling back to the phase index. */key: string; /** Left-column label, e.g. "First 3 months" / "Next 2 months" / "After that". */
187
+ label: string; /** The phase's own price, derived from its rate cards alone. */
188
+ price: PlanPriceLabel;
189
+ };
190
+ /**
191
+ * A stacked price schedule for a multi-phase plan: one row per phase, each
192
+ * priced from its own rate cards (e.g. "First 3 months — Free" then
193
+ * "After that — $750/month"). This is how an intro/ramp phase's price gets
194
+ * surfaced instead of only the steady-state price from {@link getPlanPrice}.
195
+ *
196
+ * Returns `undefined` when there is nothing to stack — fewer than two phases,
197
+ * or every phase resolving to the same price label (a free trial into a free
198
+ * plan, two identically-priced phases, …) — so callers fall back to the
199
+ * single-headline rendering.
200
+ */
201
+ declare const getPlanPriceSchedule: (plan: Plan) => PlanPriceScheduleRow[] | undefined;
202
+ //#endregion
203
+ //#region src/pricing-ui/PlanPriceSchedule.d.ts
204
+ /**
205
+ * Stacked per-phase price rows for a multi-phase plan, replacing the single
206
+ * headline price (which only reflects the steady-state phase): each row pairs
207
+ * a phase label ("First 3 months", "After that") with that phase's own price.
208
+ * Every row gets equal visual weight — the intro price is part of the plan's
209
+ * price, not a footnote.
210
+ *
211
+ * Callers derive the rows via {@link getPlanPriceSchedule} and fall back to
212
+ * the single-price rendering when it returns `undefined`. `size` picks the
213
+ * typographic treatment: `"lg"` for a card's headline area, `"sm"` for
214
+ * compact contexts (plan-change rows, summary cards).
215
+ */
216
+ declare const PlanPriceSchedule: ({
217
+ schedule,
218
+ currency,
219
+ billingCadence,
220
+ size,
221
+ className
222
+ }: {
223
+ schedule: PlanPriceScheduleRow[];
224
+ currency?: string; /** Render each priced row with the `/cadence` suffix (the plan's billing cadence). */
225
+ billingCadence?: string;
226
+ size?: "sm" | "lg";
227
+ className?: string;
228
+ }) => import("react/jsx-runtime").JSX.Element;
229
+ //#endregion
177
230
  //#region src/pricing-ui/PlanPriceTag.d.ts
178
231
  /**
179
232
  * Headline price for a plan/subscription: `$X/cadence`, "Pay as you go", or
@@ -320,6 +373,19 @@ declare const formatTieredPriceBreakdown: (opts: {
320
373
  includedLabel: string;
321
374
  }) => string[] | undefined;
322
375
  //#endregion
376
+ //#region src/utils/getPhasePriceLabel.d.ts
377
+ /**
378
+ * Headline price for a SINGLE phase, derived only from that phase's own rate
379
+ * cards. Mirrors {@link formatPlanPrice}'s rules, but scoped to the phase:
380
+ * a positive recurring flat-fee total is `priced`; otherwise a priced
381
+ * `usage_based` card in this phase makes it `payg`; otherwise it's `free`.
382
+ *
383
+ * Like {@link getPlanPrice}, one-time fees (`flat_fee` with
384
+ * `billingCadence: null`) and `price: null` rate cards contribute nothing —
385
+ * an intro phase whose fees all have `price: null` derives as `free`.
386
+ */
387
+ declare const getPhasePriceLabel: (phase: PlanPhase) => PlanPriceLabel;
388
+ //#endregion
323
389
  //#region src/utils/getPlanPrice.d.ts
324
390
  /**
325
391
  * The plan's headline recurring price: the sum of every recurring `flat_fee`
@@ -346,4 +412,4 @@ declare const collectDefaultTaxBehaviors: (plan: Plan) => CanonicalTaxBehavior;
346
412
  declare const taxBehaviorLegendSentence: (behavior: string) => string | undefined;
347
413
  declare const subscriptionTaxLegendSentence: (behavior: string) => string | undefined;
348
414
  //#endregion
349
- export { type Alignment, type BooleanEntitlementTemplate, type DynamicPrice, type EntitlementTemplate, type Feature, FeatureItem, type FlatFeeRateCard, type FlatPrice, type MeteredEntitlementTemplate, type PackagePrice, type Plan, type PlanDefaultTaxConfig, PlanEntitlements, type PlanPhase, type PlanPriceLabel, PlanPriceTag, type Price, type PriceTier, PricingCard, type PricingCardProps, PricingTable, type PricingTableProps, type ProRatingConfig, type Quota, QuotaItem, type RateCard, type StaticEntitlementTemplate, type TieredPrice, type TieredPriceBreakdownTier, type UnitPrice, type UsageBasedRateCard, type ValidationError, categorizeRateCards, collectDefaultTaxBehaviors, formatDuration, formatDurationAdjective, formatDurationInterval, formatMinorCurrencyAmount, formatPlanPrice, formatPrice, formatStaticEntitlementConfig, formatTieredPriceBreakdown, getPlanPrice, planHasDefaultTaxBehavior, subscriptionTaxLegendSentence, taxBehaviorLegendSentence };
415
+ export { type Alignment, type BooleanEntitlementTemplate, type DynamicPrice, type EntitlementTemplate, type Feature, FeatureItem, type FlatFeeRateCard, type FlatPrice, type MeteredEntitlementTemplate, type PackagePrice, type Plan, type PlanDefaultTaxConfig, PlanEntitlements, type PlanPhase, type PlanPriceLabel, PlanPriceSchedule, type PlanPriceScheduleRow, PlanPriceTag, type Price, type PriceTier, PricingCard, type PricingCardProps, PricingTable, type PricingTableProps, type ProRatingConfig, type Quota, QuotaItem, type RateCard, type StaticEntitlementTemplate, type TieredPrice, type TieredPriceBreakdownTier, type UnitPrice, type UsageBasedRateCard, type ValidationError, categorizeRateCards, collectDefaultTaxBehaviors, formatDuration, formatDurationAdjective, formatDurationInterval, formatMinorCurrencyAmount, formatPlanPrice, formatPrice, formatStaticEntitlementConfig, formatTieredPriceBreakdown, getPhasePriceLabel, getPlanPrice, getPlanPriceSchedule, planHasDefaultTaxBehavior, subscriptionTaxLegendSentence, taxBehaviorLegendSentence };
@@ -1,2 +1,2 @@
1
- import { S as formatDurationInterval, _ as formatStaticEntitlementConfig, a as planHasDefaultTaxBehavior, b as formatDuration, c as formatPlanPrice, d as PlanEntitlements, g as formatTieredPriceBreakdown, h as categorizeRateCards, i as collectDefaultTaxBehaviors, l as getPlanPrice, m as FeatureItem, n as PricingCard, o as subscriptionTaxLegendSentence, p as QuotaItem, s as taxBehaviorLegendSentence, t as PricingTable, u as PlanPriceTag, v as formatMinorCurrencyAmount, x as formatDurationAdjective, y as formatPrice } from "./PricingTable-BlcXx4-5.mjs";
2
- export { FeatureItem, PlanEntitlements, PlanPriceTag, PricingCard, PricingTable, QuotaItem, categorizeRateCards, collectDefaultTaxBehaviors, formatDuration, formatDurationAdjective, formatDurationInterval, formatMinorCurrencyAmount, formatPlanPrice, formatPrice, formatStaticEntitlementConfig, formatTieredPriceBreakdown, getPlanPrice, planHasDefaultTaxBehavior, subscriptionTaxLegendSentence, taxBehaviorLegendSentence };
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-WkG2n7V-.mjs";
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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zuplo/zudoku-plugin-monetization",
3
- "version": "0.0.42",
3
+ "version": "0.0.43",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/zuplo/zudoku",