@zuplo/zudoku-plugin-monetization 0.0.36-pre.2 → 0.0.37

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.
@@ -295,11 +295,86 @@ const PlanEntitlements = ({ phases, currency, billingCadence, units, itemClassNa
295
295
  };
296
296
  //#endregion
297
297
  //#region src/utils/getPriceFromPlan.ts
298
- const getPriceFromPlan = (plan) => {
298
+ const sumFlatFeeAmounts = (rateCards) => {
299
+ let total = 0;
300
+ for (const rc of rateCards) if (rc.type === "flat_fee" && rc.price) {
301
+ const amount = Number(rc.price.amount);
302
+ if (Number.isFinite(amount)) total += amount;
303
+ }
304
+ return total;
305
+ };
306
+ /**
307
+ * Convert an ISO 8601 duration to an approximate number of months.
308
+ * Years and months contribute exactly; weeks use 12/52 months/week and
309
+ * days use 1/30 months/day. Sub-day units (hours, minutes, seconds) do
310
+ * not contribute and a duration consisting only of those returns
311
+ * `undefined` because there is no sensible monthly equivalent.
312
+ */
313
+ const cadenceToMonths = (iso) => {
314
+ try {
315
+ const d = parse(iso);
316
+ let months = 0;
317
+ if (d.years) months += d.years * 12;
318
+ if (d.months) months += d.months;
319
+ if (d.weeks) months += d.weeks * (12 / 52);
320
+ if (d.days) months += d.days * (1 / 30);
321
+ return months > 0 ? months : void 0;
322
+ } catch {
323
+ return;
324
+ }
325
+ };
326
+ /**
327
+ * Derive a (monthly, yearly) headline price from a plan's last phase by
328
+ * summing all `flat_fee` rate-card amounts and converting from the plan's
329
+ * `billingCadence` to a monthly equivalent.
330
+ *
331
+ * Returns `null` for either field when no value can be derived (no
332
+ * phases, or an unparseable / sub-day cadence). A flat-fee sum of 0
333
+ * returns `{ monthly: 0, yearly: 0 }` (Free).
334
+ *
335
+ * Useful for consumers whose source data doesn't already include
336
+ * pre-computed `monthlyPrice` / `yearlyPrice` — pass the result through
337
+ * (or rely on `getPriceFromPlan`'s built-in fallback).
338
+ */
339
+ const derivePriceFromPlan = (plan) => {
340
+ const lastPhase = plan.phases?.at(-1);
341
+ if (!lastPhase) return {
342
+ monthly: null,
343
+ yearly: null
344
+ };
345
+ const flatPrice = sumFlatFeeAmounts(lastPhase.rateCards ?? []);
346
+ if (flatPrice === 0) return {
347
+ monthly: 0,
348
+ yearly: 0
349
+ };
350
+ const months = cadenceToMonths(plan.billingCadence);
351
+ if (months == null) return {
352
+ monthly: null,
353
+ yearly: null
354
+ };
355
+ const monthly = flatPrice / months;
299
356
  return {
357
+ monthly,
358
+ yearly: monthly * 12
359
+ };
360
+ };
361
+ /**
362
+ * Returns the monthly and yearly headline price for a plan. Prefers the
363
+ * server-provided `monthlyPrice` / `yearlyPrice` strings when present;
364
+ * otherwise falls back to {@link derivePriceFromPlan}. Values that can't
365
+ * be resolved are reported as `0`, which the pricing card renders as
366
+ * "Free".
367
+ */
368
+ const getPriceFromPlan = (plan) => {
369
+ if (plan.monthlyPrice != null || plan.yearlyPrice != null) return {
300
370
  monthly: plan.monthlyPrice != null ? parseFloat(plan.monthlyPrice) : 0,
301
371
  yearly: plan.yearlyPrice != null ? parseFloat(plan.yearlyPrice) : 0
302
372
  };
373
+ const derived = derivePriceFromPlan(plan);
374
+ return {
375
+ monthly: derived.monthly ?? 0,
376
+ yearly: derived.yearly ?? 0
377
+ };
303
378
  };
304
379
  //#endregion
305
380
  //#region src/utils/pricingTaxLegend.ts
@@ -411,7 +486,7 @@ const PricingTable = ({ plans, showYearlyPrice = true, units, renderAction, rend
411
486
  const firstPlan = plans[0];
412
487
  const taxLegendSentence = showTaxLegend && firstPlan ? taxBehaviorLegendSentence(collectDefaultTaxBehaviors(firstPlan)) : void 0;
413
488
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
414
- className: cn("w-full grid grid-cols-1 sm:grid-cols-[repeat(auto-fit,minmax(300px,max-content))] justify-center gap-6", className),
489
+ className: cn("w-full grid grid-cols-1 sm:grid-cols-[repeat(auto-fit,minmax(200px,1fr))] justify-center gap-2", className),
415
490
  children: plans.map((plan) => {
416
491
  const popular = isPopular(plan);
417
492
  const defaultCard = /* @__PURE__ */ jsx(PricingCard, {
@@ -440,4 +515,4 @@ const PricingTable = ({ plans, showYearlyPrice = true, units, renderAction, rend
440
515
  })] });
441
516
  };
442
517
  //#endregion
443
- export { formatDurationAdjective as _, subscriptionTaxLegendSentence as a, PlanEntitlements as c, categorizeRateCards as d, formatTieredPriceBreakdown as f, formatDuration as g, formatPrice as h, planHasDefaultTaxBehavior as i, QuotaItem as l, formatMinorCurrencyAmount as m, PricingCard as n, taxBehaviorLegendSentence as o, formatStaticEntitlementConfig as p, collectDefaultTaxBehaviors as r, getPriceFromPlan as s, PricingTable as t, FeatureItem as u, formatDurationInterval as v };
518
+ export { formatDuration as _, subscriptionTaxLegendSentence as a, getPriceFromPlan as c, FeatureItem as d, categorizeRateCards as f, formatPrice as g, formatMinorCurrencyAmount as h, planHasDefaultTaxBehavior as i, PlanEntitlements as l, formatStaticEntitlementConfig as m, PricingCard as n, taxBehaviorLegendSentence as o, formatTieredPriceBreakdown as p, collectDefaultTaxBehaviors as r, derivePriceFromPlan as s, PricingTable as t, QuotaItem as u, formatDurationAdjective as v, formatDurationInterval as y };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as formatDurationAdjective, a as subscriptionTaxLegendSentence, c as PlanEntitlements, d as categorizeRateCards, f as formatTieredPriceBreakdown, g as formatDuration, h as formatPrice, i as planHasDefaultTaxBehavior, m as formatMinorCurrencyAmount, p as formatStaticEntitlementConfig, s as getPriceFromPlan, t as PricingTable, v as formatDurationInterval } from "./PricingTable-DfYAmAjk.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-Dsbd3E2Q.mjs";
2
2
  import { Suspense, createContext, use, useEffect, useMemo, useState } from "react";
3
3
  import { cn, createPlugin, joinUrl, throwIfProblemJson } from "zudoku";
4
4
  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";
@@ -517,26 +517,23 @@ const PricingPageSkeleton = () => /* @__PURE__ */ jsxs("div", {
517
517
  className: "text-center space-y-4 mb-12",
518
518
  children: [/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-48 mx-auto" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-96 mx-auto" })]
519
519
  }), /* @__PURE__ */ jsx("div", {
520
- className: "w-full grid grid-cols-1 sm:grid-cols-[repeat(auto-fit,minmax(300px,max-content))] justify-center gap-6",
520
+ className: "w-full grid grid-cols-1 sm:grid-cols-[repeat(auto-fit,minmax(200px,1fr))] justify-center gap-2",
521
521
  children: [
522
522
  1,
523
523
  2,
524
524
  3
525
- ].map((i) => /* @__PURE__ */ jsxs(Card, {
526
- className: "w-[300px]",
527
- children: [/* @__PURE__ */ jsxs(CardHeader, {
528
- className: "space-y-3",
529
- children: [/* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-24" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-32" })]
530
- }), /* @__PURE__ */ jsxs(CardContent, {
531
- className: "space-y-3",
532
- children: [
533
- /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full" }),
534
- /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full" }),
535
- /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-3/4" }),
536
- /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full mt-4" })
537
- ]
538
- })]
539
- }, i))
525
+ ].map((i) => /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsxs(CardHeader, {
526
+ className: "space-y-3",
527
+ children: [/* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-24" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-32" })]
528
+ }), /* @__PURE__ */ jsxs(CardContent, {
529
+ className: "space-y-3",
530
+ children: [
531
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full" }),
532
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full" }),
533
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-3/4" }),
534
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full mt-4" })
535
+ ]
536
+ })] }, i))
540
537
  })]
541
538
  });
542
539
  //#endregion
@@ -281,6 +281,30 @@ declare const formatTieredPriceBreakdown: (opts: {
281
281
  }) => string[] | undefined;
282
282
  //#endregion
283
283
  //#region src/utils/getPriceFromPlan.d.ts
284
+ /**
285
+ * Derive a (monthly, yearly) headline price from a plan's last phase by
286
+ * summing all `flat_fee` rate-card amounts and converting from the plan's
287
+ * `billingCadence` to a monthly equivalent.
288
+ *
289
+ * Returns `null` for either field when no value can be derived (no
290
+ * phases, or an unparseable / sub-day cadence). A flat-fee sum of 0
291
+ * returns `{ monthly: 0, yearly: 0 }` (Free).
292
+ *
293
+ * Useful for consumers whose source data doesn't already include
294
+ * pre-computed `monthlyPrice` / `yearlyPrice` — pass the result through
295
+ * (or rely on `getPriceFromPlan`'s built-in fallback).
296
+ */
297
+ declare const derivePriceFromPlan: (plan: Plan) => {
298
+ monthly: number | null;
299
+ yearly: number | null;
300
+ };
301
+ /**
302
+ * Returns the monthly and yearly headline price for a plan. Prefers the
303
+ * server-provided `monthlyPrice` / `yearlyPrice` strings when present;
304
+ * otherwise falls back to {@link derivePriceFromPlan}. Values that can't
305
+ * be resolved are reported as `0`, which the pricing card renders as
306
+ * "Free".
307
+ */
284
308
  declare const getPriceFromPlan: (plan: Plan) => {
285
309
  monthly: number;
286
310
  yearly: number;
@@ -293,4 +317,4 @@ declare const collectDefaultTaxBehaviors: (plan: Plan) => CanonicalTaxBehavior;
293
317
  declare const taxBehaviorLegendSentence: (behavior: string) => string | undefined;
294
318
  declare const subscriptionTaxLegendSentence: (behavior: string) => string | undefined;
295
319
  //#endregion
296
- 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 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, formatPrice, formatStaticEntitlementConfig, formatTieredPriceBreakdown, getPriceFromPlan, planHasDefaultTaxBehavior, subscriptionTaxLegendSentence, taxBehaviorLegendSentence };
320
+ 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 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, derivePriceFromPlan, formatDuration, formatDurationAdjective, formatDurationInterval, formatMinorCurrencyAmount, formatPrice, formatStaticEntitlementConfig, formatTieredPriceBreakdown, getPriceFromPlan, planHasDefaultTaxBehavior, subscriptionTaxLegendSentence, taxBehaviorLegendSentence };
@@ -1,2 +1,2 @@
1
- import { _ as formatDurationAdjective, a as subscriptionTaxLegendSentence, c as PlanEntitlements, d as categorizeRateCards, f as formatTieredPriceBreakdown, g as formatDuration, h as formatPrice, i as planHasDefaultTaxBehavior, l as QuotaItem, m as formatMinorCurrencyAmount, n as PricingCard, o as taxBehaviorLegendSentence, p as formatStaticEntitlementConfig, r as collectDefaultTaxBehaviors, s as getPriceFromPlan, t as PricingTable, u as FeatureItem, v as formatDurationInterval } from "./PricingTable-DfYAmAjk.mjs";
2
- export { FeatureItem, PlanEntitlements, PricingCard, PricingTable, QuotaItem, categorizeRateCards, collectDefaultTaxBehaviors, formatDuration, formatDurationAdjective, formatDurationInterval, formatMinorCurrencyAmount, formatPrice, formatStaticEntitlementConfig, formatTieredPriceBreakdown, getPriceFromPlan, planHasDefaultTaxBehavior, subscriptionTaxLegendSentence, taxBehaviorLegendSentence };
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";
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.36-pre.2",
3
+ "version": "0.0.37",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/zuplo/zudoku",
@@ -24,7 +24,7 @@
24
24
  ],
25
25
  "dependencies": {
26
26
  "clsx": "2.1.1",
27
- "tailwind-merge": "3.5.0",
27
+ "tailwind-merge": "3.6.0",
28
28
  "tinyduration": "3.4.1"
29
29
  },
30
30
  "devDependencies": {