@aforoai/storefront-widgets 1.0.4 → 1.0.5

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.
@@ -27,7 +27,7 @@ var React7__namespace = /*#__PURE__*/_interopNamespace(React7);
27
27
  // src/vanilla/index.ts
28
28
 
29
29
  // src/core/version.ts
30
- var VERSION = "1.0.4";
30
+ var VERSION = "1.0.5";
31
31
 
32
32
  // src/core/AforoEmbed.ts
33
33
  var mountedElements = /* @__PURE__ */ new WeakMap();
@@ -547,7 +547,8 @@ var _BridgeClient = class _BridgeClient {
547
547
  throw await this.parseError(resp);
548
548
  }
549
549
  const body = await resp.json();
550
- if (!body.sessionJwt || typeof body.expiresAt !== "number") {
550
+ const data = body?.data ?? body;
551
+ if (!data || !data.sessionJwt || typeof data.expiresAt !== "number") {
551
552
  throw new BridgeClientError(
552
553
  "Malformed bridge exchange response",
553
554
  500,
@@ -556,9 +557,9 @@ var _BridgeClient = class _BridgeClient {
556
557
  );
557
558
  }
558
559
  return {
559
- sessionJwt: body.sessionJwt,
560
- expiresAt: body.expiresAt,
561
- customerId: body.customerId ?? null
560
+ sessionJwt: data.sessionJwt,
561
+ expiresAt: data.expiresAt,
562
+ customerId: data.customerId ?? null
562
563
  };
563
564
  }
564
565
  /**
@@ -644,16 +645,17 @@ var _BridgeClient = class _BridgeClient {
644
645
  try {
645
646
  const body = await resp.json();
646
647
  if (!body || typeof body !== "object") return null;
648
+ const data = body.data ?? body;
647
649
  return {
648
- tenantSlug: body.tenantSlug,
650
+ tenantSlug: data.tenantSlug,
649
651
  branding: {
650
- primaryColor: body.primaryColor ?? null,
651
- secondaryColor: body.secondaryColor ?? null,
652
- logoUrl: body.logoUrl ?? null,
653
- fontFamily: body.fontFamily ?? null
652
+ primaryColor: data.primaryColor ?? null,
653
+ secondaryColor: data.secondaryColor ?? null,
654
+ logoUrl: data.logoUrl ?? null,
655
+ fontFamily: data.fontFamily ?? null
654
656
  },
655
- offerings: Array.isArray(body.offerings) ? body.offerings : [],
656
- embeddableWidget: body.embeddableWidget ?? null
657
+ offerings: Array.isArray(data.offerings) ? data.offerings : [],
658
+ embeddableWidget: data.embeddableWidget ?? null
657
659
  };
658
660
  } catch {
659
661
  return null;
@@ -798,7 +800,8 @@ var _BridgeClient = class _BridgeClient {
798
800
  false
799
801
  );
800
802
  }
801
- if (!body.checkoutUrl || !body.checkoutSessionId || !body.expiresAt) {
803
+ const data = body?.data ?? body ?? {};
804
+ if (!data.checkoutUrl || !data.checkoutSessionId || !data.expiresAt) {
802
805
  throw new BridgeClientError(
803
806
  "Malformed checkout-initiate response (missing required fields)",
804
807
  500,
@@ -807,9 +810,9 @@ var _BridgeClient = class _BridgeClient {
807
810
  );
808
811
  }
809
812
  return {
810
- checkoutUrl: body.checkoutUrl,
811
- checkoutSessionId: body.checkoutSessionId,
812
- expiresAt: body.expiresAt
813
+ checkoutUrl: data.checkoutUrl,
814
+ checkoutSessionId: data.checkoutSessionId,
815
+ expiresAt: data.expiresAt
813
816
  };
814
817
  }
815
818
  /* ────────────────────────────────────────────────────────────────────
@@ -1617,13 +1620,16 @@ var _BridgeClient = class _BridgeClient {
1617
1620
  if (!resp.ok) return emptyPage;
1618
1621
  try {
1619
1622
  const body = await resp.json();
1620
- if (!body || typeof body !== "object") return emptyPage;
1623
+ if (!body || typeof body !== "object") {
1624
+ return emptyPage;
1625
+ }
1626
+ const data = body.data ?? body;
1621
1627
  return {
1622
- content: Array.isArray(body.content) ? body.content : [],
1623
- totalElements: typeof body.totalElements === "number" && Number.isFinite(body.totalElements) ? body.totalElements : 0,
1624
- totalPages: typeof body.totalPages === "number" && Number.isFinite(body.totalPages) ? body.totalPages : 0,
1625
- page: typeof body.page === "number" && Number.isFinite(body.page) ? body.page : filter.page ?? 0,
1626
- size: typeof body.size === "number" && Number.isFinite(body.size) ? body.size : filter.size ?? 10
1628
+ content: Array.isArray(data.content) ? data.content : [],
1629
+ totalElements: typeof data.totalElements === "number" && Number.isFinite(data.totalElements) ? data.totalElements : 0,
1630
+ totalPages: typeof data.totalPages === "number" && Number.isFinite(data.totalPages) ? data.totalPages : 0,
1631
+ page: typeof data.page === "number" && Number.isFinite(data.page) ? data.page : filter.page ?? 0,
1632
+ size: typeof data.size === "number" && Number.isFinite(data.size) ? data.size : filter.size ?? 10
1627
1633
  };
1628
1634
  } catch {
1629
1635
  return emptyPage;
@@ -3127,6 +3133,20 @@ function safeFormatCurrency(priceCents, currency, locale, intlOverride) {
3127
3133
  return `${currency} ${amount.toFixed(2)}`;
3128
3134
  }
3129
3135
  }
3136
+ function resolveDisplayPrice(offering, locale, intlOverride) {
3137
+ const flat = safeFormatCurrency(offering.priceCents, offering.currency, locale, intlOverride);
3138
+ if (flat !== "\u2014") return { text: flat, unitSuffix: null };
3139
+ const perUnitPlan = offering.ratePlans?.find(
3140
+ (rp) => rp.pricingModel === "PER_UNIT" && rp.perUnitPriceCents != null
3141
+ );
3142
+ if (perUnitPlan) {
3143
+ const unitPrice = safeFormatCurrency(perUnitPlan.perUnitPriceCents, offering.currency, locale, intlOverride);
3144
+ if (unitPrice !== "\u2014") {
3145
+ return { text: unitPrice, unitSuffix: perUnitPlan.unitLabel || "unit" };
3146
+ }
3147
+ }
3148
+ return { text: "\u2014", unitSuffix: null };
3149
+ }
3130
3150
  function localizedName(o) {
3131
3151
  return o.localizedDisplayName || o.name || "(unnamed plan)";
3132
3152
  }
@@ -3530,12 +3550,7 @@ function PlanCard({
3530
3550
  onCtaClick,
3531
3551
  intlOverride
3532
3552
  }) {
3533
- const price = safeFormatCurrency(
3534
- offering.priceCents,
3535
- offering.currency,
3536
- locale,
3537
- intlOverride
3538
- );
3553
+ const { text: price, unitSuffix } = resolveDisplayPrice(offering, locale, intlOverride);
3539
3554
  const isContactSales = price === "\u2014";
3540
3555
  const planName = localizedName(offering);
3541
3556
  const description = localizedDescription(offering);
@@ -3556,7 +3571,7 @@ function PlanCard({
3556
3571
  "div",
3557
3572
  {
3558
3573
  role: "group",
3559
- "aria-label": `${planName}, ${price} per ${offering.billingCycle}`,
3574
+ "aria-label": `${planName}, ${price} per ${unitSuffix ?? offering.billingCycle}`,
3560
3575
  className: `aforo-w-pc-card${featured ? " aforo-w-pc-card-featured" : ""}`,
3561
3576
  style: cardStyle2,
3562
3577
  children: [
@@ -3621,14 +3636,14 @@ function PlanCard({
3621
3636
  children: price
3622
3637
  }
3623
3638
  ),
3624
- !isContactSales && offering.billingCycle ? /* @__PURE__ */ jsxRuntime.jsxs(
3639
+ !isContactSales && (unitSuffix || offering.billingCycle) ? /* @__PURE__ */ jsxRuntime.jsxs(
3625
3640
  "span",
3626
3641
  {
3627
3642
  className: "aforo-w-pc-cycle",
3628
3643
  style: { fontSize: 13, color: tokens.textMuted },
3629
3644
  children: [
3630
3645
  "/ ",
3631
- humanCycle(offering.billingCycle)
3646
+ unitSuffix ?? humanCycle(offering.billingCycle)
3632
3647
  ]
3633
3648
  }
3634
3649
  ) : null
@@ -3786,6 +3801,7 @@ function TableLayout({
3786
3801
  /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", style: { ...headerCellStyle, color: tokens.textMuted, fontSize: 12 }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "aforo-w-pc-sr-only", style: srOnlyStyle, children: "Feature" }) }),
3787
3802
  offerings.map((o) => {
3788
3803
  const featured = offeringIsFeatured(o, featuredOverride);
3804
+ const { text: tablePrice, unitSuffix: tableUnitSuffix } = resolveDisplayPrice(o, locale, intlOverride);
3789
3805
  return /* @__PURE__ */ jsxRuntime.jsx(
3790
3806
  "th",
3791
3807
  {
@@ -3815,13 +3831,20 @@ function TableLayout({
3815
3831
  children: "Most popular"
3816
3832
  }
3817
3833
  ) : null,
3818
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 22, fontWeight: 700 }, children: safeFormatCurrency(o.priceCents, o.currency, locale, intlOverride) }),
3834
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 22, fontWeight: 700 }, children: [
3835
+ tablePrice,
3836
+ tableUnitSuffix ? /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontSize: 12, fontWeight: 400, color: tokens.textMuted }, children: [
3837
+ " ",
3838
+ "/ ",
3839
+ tableUnitSuffix
3840
+ ] }) : null
3841
+ ] }),
3819
3842
  /* @__PURE__ */ jsxRuntime.jsx(
3820
3843
  "button",
3821
3844
  {
3822
3845
  type: "button",
3823
3846
  onClick: () => onCtaClick(o),
3824
- "aria-label": `${ctaLabel(o, ctaTextProp, safeFormatCurrency(o.priceCents, o.currency, locale, intlOverride) === "\u2014")} for ${localizedName(o)}`,
3847
+ "aria-label": `${ctaLabel(o, ctaTextProp, tablePrice === "\u2014")} for ${localizedName(o)}`,
3825
3848
  style: {
3826
3849
  inlineSize: "100%",
3827
3850
  padding: "8px 12px",
@@ -3834,7 +3857,7 @@ function TableLayout({
3834
3857
  cursor: "pointer",
3835
3858
  fontFamily: tokens.fontFamily
3836
3859
  },
3837
- children: ctaLabel(o, ctaTextProp, safeFormatCurrency(o.priceCents, o.currency, locale, intlOverride) === "\u2014")
3860
+ children: ctaLabel(o, ctaTextProp, tablePrice === "\u2014")
3838
3861
  }
3839
3862
  )
3840
3863
  ] })
@@ -5,7 +5,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
  // src/vanilla/index.ts
6
6
 
7
7
  // src/core/version.ts
8
- var VERSION = "1.0.4";
8
+ var VERSION = "1.0.5";
9
9
 
10
10
  // src/core/AforoEmbed.ts
11
11
  var mountedElements = /* @__PURE__ */ new WeakMap();
@@ -525,7 +525,8 @@ var _BridgeClient = class _BridgeClient {
525
525
  throw await this.parseError(resp);
526
526
  }
527
527
  const body = await resp.json();
528
- if (!body.sessionJwt || typeof body.expiresAt !== "number") {
528
+ const data = body?.data ?? body;
529
+ if (!data || !data.sessionJwt || typeof data.expiresAt !== "number") {
529
530
  throw new BridgeClientError(
530
531
  "Malformed bridge exchange response",
531
532
  500,
@@ -534,9 +535,9 @@ var _BridgeClient = class _BridgeClient {
534
535
  );
535
536
  }
536
537
  return {
537
- sessionJwt: body.sessionJwt,
538
- expiresAt: body.expiresAt,
539
- customerId: body.customerId ?? null
538
+ sessionJwt: data.sessionJwt,
539
+ expiresAt: data.expiresAt,
540
+ customerId: data.customerId ?? null
540
541
  };
541
542
  }
542
543
  /**
@@ -622,16 +623,17 @@ var _BridgeClient = class _BridgeClient {
622
623
  try {
623
624
  const body = await resp.json();
624
625
  if (!body || typeof body !== "object") return null;
626
+ const data = body.data ?? body;
625
627
  return {
626
- tenantSlug: body.tenantSlug,
628
+ tenantSlug: data.tenantSlug,
627
629
  branding: {
628
- primaryColor: body.primaryColor ?? null,
629
- secondaryColor: body.secondaryColor ?? null,
630
- logoUrl: body.logoUrl ?? null,
631
- fontFamily: body.fontFamily ?? null
630
+ primaryColor: data.primaryColor ?? null,
631
+ secondaryColor: data.secondaryColor ?? null,
632
+ logoUrl: data.logoUrl ?? null,
633
+ fontFamily: data.fontFamily ?? null
632
634
  },
633
- offerings: Array.isArray(body.offerings) ? body.offerings : [],
634
- embeddableWidget: body.embeddableWidget ?? null
635
+ offerings: Array.isArray(data.offerings) ? data.offerings : [],
636
+ embeddableWidget: data.embeddableWidget ?? null
635
637
  };
636
638
  } catch {
637
639
  return null;
@@ -776,7 +778,8 @@ var _BridgeClient = class _BridgeClient {
776
778
  false
777
779
  );
778
780
  }
779
- if (!body.checkoutUrl || !body.checkoutSessionId || !body.expiresAt) {
781
+ const data = body?.data ?? body ?? {};
782
+ if (!data.checkoutUrl || !data.checkoutSessionId || !data.expiresAt) {
780
783
  throw new BridgeClientError(
781
784
  "Malformed checkout-initiate response (missing required fields)",
782
785
  500,
@@ -785,9 +788,9 @@ var _BridgeClient = class _BridgeClient {
785
788
  );
786
789
  }
787
790
  return {
788
- checkoutUrl: body.checkoutUrl,
789
- checkoutSessionId: body.checkoutSessionId,
790
- expiresAt: body.expiresAt
791
+ checkoutUrl: data.checkoutUrl,
792
+ checkoutSessionId: data.checkoutSessionId,
793
+ expiresAt: data.expiresAt
791
794
  };
792
795
  }
793
796
  /* ────────────────────────────────────────────────────────────────────
@@ -1595,13 +1598,16 @@ var _BridgeClient = class _BridgeClient {
1595
1598
  if (!resp.ok) return emptyPage;
1596
1599
  try {
1597
1600
  const body = await resp.json();
1598
- if (!body || typeof body !== "object") return emptyPage;
1601
+ if (!body || typeof body !== "object") {
1602
+ return emptyPage;
1603
+ }
1604
+ const data = body.data ?? body;
1599
1605
  return {
1600
- content: Array.isArray(body.content) ? body.content : [],
1601
- totalElements: typeof body.totalElements === "number" && Number.isFinite(body.totalElements) ? body.totalElements : 0,
1602
- totalPages: typeof body.totalPages === "number" && Number.isFinite(body.totalPages) ? body.totalPages : 0,
1603
- page: typeof body.page === "number" && Number.isFinite(body.page) ? body.page : filter.page ?? 0,
1604
- size: typeof body.size === "number" && Number.isFinite(body.size) ? body.size : filter.size ?? 10
1606
+ content: Array.isArray(data.content) ? data.content : [],
1607
+ totalElements: typeof data.totalElements === "number" && Number.isFinite(data.totalElements) ? data.totalElements : 0,
1608
+ totalPages: typeof data.totalPages === "number" && Number.isFinite(data.totalPages) ? data.totalPages : 0,
1609
+ page: typeof data.page === "number" && Number.isFinite(data.page) ? data.page : filter.page ?? 0,
1610
+ size: typeof data.size === "number" && Number.isFinite(data.size) ? data.size : filter.size ?? 10
1605
1611
  };
1606
1612
  } catch {
1607
1613
  return emptyPage;
@@ -3105,6 +3111,20 @@ function safeFormatCurrency(priceCents, currency, locale, intlOverride) {
3105
3111
  return `${currency} ${amount.toFixed(2)}`;
3106
3112
  }
3107
3113
  }
3114
+ function resolveDisplayPrice(offering, locale, intlOverride) {
3115
+ const flat = safeFormatCurrency(offering.priceCents, offering.currency, locale, intlOverride);
3116
+ if (flat !== "\u2014") return { text: flat, unitSuffix: null };
3117
+ const perUnitPlan = offering.ratePlans?.find(
3118
+ (rp) => rp.pricingModel === "PER_UNIT" && rp.perUnitPriceCents != null
3119
+ );
3120
+ if (perUnitPlan) {
3121
+ const unitPrice = safeFormatCurrency(perUnitPlan.perUnitPriceCents, offering.currency, locale, intlOverride);
3122
+ if (unitPrice !== "\u2014") {
3123
+ return { text: unitPrice, unitSuffix: perUnitPlan.unitLabel || "unit" };
3124
+ }
3125
+ }
3126
+ return { text: "\u2014", unitSuffix: null };
3127
+ }
3108
3128
  function localizedName(o) {
3109
3129
  return o.localizedDisplayName || o.name || "(unnamed plan)";
3110
3130
  }
@@ -3508,12 +3528,7 @@ function PlanCard({
3508
3528
  onCtaClick,
3509
3529
  intlOverride
3510
3530
  }) {
3511
- const price = safeFormatCurrency(
3512
- offering.priceCents,
3513
- offering.currency,
3514
- locale,
3515
- intlOverride
3516
- );
3531
+ const { text: price, unitSuffix } = resolveDisplayPrice(offering, locale, intlOverride);
3517
3532
  const isContactSales = price === "\u2014";
3518
3533
  const planName = localizedName(offering);
3519
3534
  const description = localizedDescription(offering);
@@ -3534,7 +3549,7 @@ function PlanCard({
3534
3549
  "div",
3535
3550
  {
3536
3551
  role: "group",
3537
- "aria-label": `${planName}, ${price} per ${offering.billingCycle}`,
3552
+ "aria-label": `${planName}, ${price} per ${unitSuffix ?? offering.billingCycle}`,
3538
3553
  className: `aforo-w-pc-card${featured ? " aforo-w-pc-card-featured" : ""}`,
3539
3554
  style: cardStyle2,
3540
3555
  children: [
@@ -3599,14 +3614,14 @@ function PlanCard({
3599
3614
  children: price
3600
3615
  }
3601
3616
  ),
3602
- !isContactSales && offering.billingCycle ? /* @__PURE__ */ jsxs(
3617
+ !isContactSales && (unitSuffix || offering.billingCycle) ? /* @__PURE__ */ jsxs(
3603
3618
  "span",
3604
3619
  {
3605
3620
  className: "aforo-w-pc-cycle",
3606
3621
  style: { fontSize: 13, color: tokens.textMuted },
3607
3622
  children: [
3608
3623
  "/ ",
3609
- humanCycle(offering.billingCycle)
3624
+ unitSuffix ?? humanCycle(offering.billingCycle)
3610
3625
  ]
3611
3626
  }
3612
3627
  ) : null
@@ -3764,6 +3779,7 @@ function TableLayout({
3764
3779
  /* @__PURE__ */ jsx("th", { scope: "col", style: { ...headerCellStyle, color: tokens.textMuted, fontSize: 12 }, children: /* @__PURE__ */ jsx("span", { className: "aforo-w-pc-sr-only", style: srOnlyStyle, children: "Feature" }) }),
3765
3780
  offerings.map((o) => {
3766
3781
  const featured = offeringIsFeatured(o, featuredOverride);
3782
+ const { text: tablePrice, unitSuffix: tableUnitSuffix } = resolveDisplayPrice(o, locale, intlOverride);
3767
3783
  return /* @__PURE__ */ jsx(
3768
3784
  "th",
3769
3785
  {
@@ -3793,13 +3809,20 @@ function TableLayout({
3793
3809
  children: "Most popular"
3794
3810
  }
3795
3811
  ) : null,
3796
- /* @__PURE__ */ jsx("div", { style: { fontSize: 22, fontWeight: 700 }, children: safeFormatCurrency(o.priceCents, o.currency, locale, intlOverride) }),
3812
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: 22, fontWeight: 700 }, children: [
3813
+ tablePrice,
3814
+ tableUnitSuffix ? /* @__PURE__ */ jsxs("span", { style: { fontSize: 12, fontWeight: 400, color: tokens.textMuted }, children: [
3815
+ " ",
3816
+ "/ ",
3817
+ tableUnitSuffix
3818
+ ] }) : null
3819
+ ] }),
3797
3820
  /* @__PURE__ */ jsx(
3798
3821
  "button",
3799
3822
  {
3800
3823
  type: "button",
3801
3824
  onClick: () => onCtaClick(o),
3802
- "aria-label": `${ctaLabel(o, ctaTextProp, safeFormatCurrency(o.priceCents, o.currency, locale, intlOverride) === "\u2014")} for ${localizedName(o)}`,
3825
+ "aria-label": `${ctaLabel(o, ctaTextProp, tablePrice === "\u2014")} for ${localizedName(o)}`,
3803
3826
  style: {
3804
3827
  inlineSize: "100%",
3805
3828
  padding: "8px 12px",
@@ -3812,7 +3835,7 @@ function TableLayout({
3812
3835
  cursor: "pointer",
3813
3836
  fontFamily: tokens.fontFamily
3814
3837
  },
3815
- children: ctaLabel(o, ctaTextProp, safeFormatCurrency(o.priceCents, o.currency, locale, intlOverride) === "\u2014")
3838
+ children: ctaLabel(o, ctaTextProp, tablePrice === "\u2014")
3816
3839
  }
3817
3840
  )
3818
3841
  ] })
@@ -28,7 +28,7 @@ var React7__namespace = /*#__PURE__*/_interopNamespace(React7);
28
28
  // src/vue/index.ts
29
29
 
30
30
  // src/core/version.ts
31
- var VERSION = "1.0.4";
31
+ var VERSION = "1.0.5";
32
32
 
33
33
  // src/core/AforoEmbed.ts
34
34
  var mountedElements = /* @__PURE__ */ new WeakMap();
@@ -548,7 +548,8 @@ var _BridgeClient = class _BridgeClient {
548
548
  throw await this.parseError(resp);
549
549
  }
550
550
  const body = await resp.json();
551
- if (!body.sessionJwt || typeof body.expiresAt !== "number") {
551
+ const data = body?.data ?? body;
552
+ if (!data || !data.sessionJwt || typeof data.expiresAt !== "number") {
552
553
  throw new BridgeClientError(
553
554
  "Malformed bridge exchange response",
554
555
  500,
@@ -557,9 +558,9 @@ var _BridgeClient = class _BridgeClient {
557
558
  );
558
559
  }
559
560
  return {
560
- sessionJwt: body.sessionJwt,
561
- expiresAt: body.expiresAt,
562
- customerId: body.customerId ?? null
561
+ sessionJwt: data.sessionJwt,
562
+ expiresAt: data.expiresAt,
563
+ customerId: data.customerId ?? null
563
564
  };
564
565
  }
565
566
  /**
@@ -645,16 +646,17 @@ var _BridgeClient = class _BridgeClient {
645
646
  try {
646
647
  const body = await resp.json();
647
648
  if (!body || typeof body !== "object") return null;
649
+ const data = body.data ?? body;
648
650
  return {
649
- tenantSlug: body.tenantSlug,
651
+ tenantSlug: data.tenantSlug,
650
652
  branding: {
651
- primaryColor: body.primaryColor ?? null,
652
- secondaryColor: body.secondaryColor ?? null,
653
- logoUrl: body.logoUrl ?? null,
654
- fontFamily: body.fontFamily ?? null
653
+ primaryColor: data.primaryColor ?? null,
654
+ secondaryColor: data.secondaryColor ?? null,
655
+ logoUrl: data.logoUrl ?? null,
656
+ fontFamily: data.fontFamily ?? null
655
657
  },
656
- offerings: Array.isArray(body.offerings) ? body.offerings : [],
657
- embeddableWidget: body.embeddableWidget ?? null
658
+ offerings: Array.isArray(data.offerings) ? data.offerings : [],
659
+ embeddableWidget: data.embeddableWidget ?? null
658
660
  };
659
661
  } catch {
660
662
  return null;
@@ -799,7 +801,8 @@ var _BridgeClient = class _BridgeClient {
799
801
  false
800
802
  );
801
803
  }
802
- if (!body.checkoutUrl || !body.checkoutSessionId || !body.expiresAt) {
804
+ const data = body?.data ?? body ?? {};
805
+ if (!data.checkoutUrl || !data.checkoutSessionId || !data.expiresAt) {
803
806
  throw new BridgeClientError(
804
807
  "Malformed checkout-initiate response (missing required fields)",
805
808
  500,
@@ -808,9 +811,9 @@ var _BridgeClient = class _BridgeClient {
808
811
  );
809
812
  }
810
813
  return {
811
- checkoutUrl: body.checkoutUrl,
812
- checkoutSessionId: body.checkoutSessionId,
813
- expiresAt: body.expiresAt
814
+ checkoutUrl: data.checkoutUrl,
815
+ checkoutSessionId: data.checkoutSessionId,
816
+ expiresAt: data.expiresAt
814
817
  };
815
818
  }
816
819
  /* ────────────────────────────────────────────────────────────────────
@@ -1618,13 +1621,16 @@ var _BridgeClient = class _BridgeClient {
1618
1621
  if (!resp.ok) return emptyPage;
1619
1622
  try {
1620
1623
  const body = await resp.json();
1621
- if (!body || typeof body !== "object") return emptyPage;
1624
+ if (!body || typeof body !== "object") {
1625
+ return emptyPage;
1626
+ }
1627
+ const data = body.data ?? body;
1622
1628
  return {
1623
- content: Array.isArray(body.content) ? body.content : [],
1624
- totalElements: typeof body.totalElements === "number" && Number.isFinite(body.totalElements) ? body.totalElements : 0,
1625
- totalPages: typeof body.totalPages === "number" && Number.isFinite(body.totalPages) ? body.totalPages : 0,
1626
- page: typeof body.page === "number" && Number.isFinite(body.page) ? body.page : filter.page ?? 0,
1627
- size: typeof body.size === "number" && Number.isFinite(body.size) ? body.size : filter.size ?? 10
1629
+ content: Array.isArray(data.content) ? data.content : [],
1630
+ totalElements: typeof data.totalElements === "number" && Number.isFinite(data.totalElements) ? data.totalElements : 0,
1631
+ totalPages: typeof data.totalPages === "number" && Number.isFinite(data.totalPages) ? data.totalPages : 0,
1632
+ page: typeof data.page === "number" && Number.isFinite(data.page) ? data.page : filter.page ?? 0,
1633
+ size: typeof data.size === "number" && Number.isFinite(data.size) ? data.size : filter.size ?? 10
1628
1634
  };
1629
1635
  } catch {
1630
1636
  return emptyPage;
@@ -3128,6 +3134,20 @@ function safeFormatCurrency(priceCents, currency, locale, intlOverride) {
3128
3134
  return `${currency} ${amount.toFixed(2)}`;
3129
3135
  }
3130
3136
  }
3137
+ function resolveDisplayPrice(offering, locale, intlOverride) {
3138
+ const flat = safeFormatCurrency(offering.priceCents, offering.currency, locale, intlOverride);
3139
+ if (flat !== "\u2014") return { text: flat, unitSuffix: null };
3140
+ const perUnitPlan = offering.ratePlans?.find(
3141
+ (rp) => rp.pricingModel === "PER_UNIT" && rp.perUnitPriceCents != null
3142
+ );
3143
+ if (perUnitPlan) {
3144
+ const unitPrice = safeFormatCurrency(perUnitPlan.perUnitPriceCents, offering.currency, locale, intlOverride);
3145
+ if (unitPrice !== "\u2014") {
3146
+ return { text: unitPrice, unitSuffix: perUnitPlan.unitLabel || "unit" };
3147
+ }
3148
+ }
3149
+ return { text: "\u2014", unitSuffix: null };
3150
+ }
3131
3151
  function localizedName(o) {
3132
3152
  return o.localizedDisplayName || o.name || "(unnamed plan)";
3133
3153
  }
@@ -3531,12 +3551,7 @@ function PlanCard({
3531
3551
  onCtaClick,
3532
3552
  intlOverride
3533
3553
  }) {
3534
- const price = safeFormatCurrency(
3535
- offering.priceCents,
3536
- offering.currency,
3537
- locale,
3538
- intlOverride
3539
- );
3554
+ const { text: price, unitSuffix } = resolveDisplayPrice(offering, locale, intlOverride);
3540
3555
  const isContactSales = price === "\u2014";
3541
3556
  const planName = localizedName(offering);
3542
3557
  const description = localizedDescription(offering);
@@ -3557,7 +3572,7 @@ function PlanCard({
3557
3572
  "div",
3558
3573
  {
3559
3574
  role: "group",
3560
- "aria-label": `${planName}, ${price} per ${offering.billingCycle}`,
3575
+ "aria-label": `${planName}, ${price} per ${unitSuffix ?? offering.billingCycle}`,
3561
3576
  className: `aforo-w-pc-card${featured ? " aforo-w-pc-card-featured" : ""}`,
3562
3577
  style: cardStyle2,
3563
3578
  children: [
@@ -3622,14 +3637,14 @@ function PlanCard({
3622
3637
  children: price
3623
3638
  }
3624
3639
  ),
3625
- !isContactSales && offering.billingCycle ? /* @__PURE__ */ jsxRuntime.jsxs(
3640
+ !isContactSales && (unitSuffix || offering.billingCycle) ? /* @__PURE__ */ jsxRuntime.jsxs(
3626
3641
  "span",
3627
3642
  {
3628
3643
  className: "aforo-w-pc-cycle",
3629
3644
  style: { fontSize: 13, color: tokens.textMuted },
3630
3645
  children: [
3631
3646
  "/ ",
3632
- humanCycle(offering.billingCycle)
3647
+ unitSuffix ?? humanCycle(offering.billingCycle)
3633
3648
  ]
3634
3649
  }
3635
3650
  ) : null
@@ -3787,6 +3802,7 @@ function TableLayout({
3787
3802
  /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", style: { ...headerCellStyle, color: tokens.textMuted, fontSize: 12 }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "aforo-w-pc-sr-only", style: srOnlyStyle, children: "Feature" }) }),
3788
3803
  offerings.map((o) => {
3789
3804
  const featured = offeringIsFeatured(o, featuredOverride);
3805
+ const { text: tablePrice, unitSuffix: tableUnitSuffix } = resolveDisplayPrice(o, locale, intlOverride);
3790
3806
  return /* @__PURE__ */ jsxRuntime.jsx(
3791
3807
  "th",
3792
3808
  {
@@ -3816,13 +3832,20 @@ function TableLayout({
3816
3832
  children: "Most popular"
3817
3833
  }
3818
3834
  ) : null,
3819
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 22, fontWeight: 700 }, children: safeFormatCurrency(o.priceCents, o.currency, locale, intlOverride) }),
3835
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 22, fontWeight: 700 }, children: [
3836
+ tablePrice,
3837
+ tableUnitSuffix ? /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontSize: 12, fontWeight: 400, color: tokens.textMuted }, children: [
3838
+ " ",
3839
+ "/ ",
3840
+ tableUnitSuffix
3841
+ ] }) : null
3842
+ ] }),
3820
3843
  /* @__PURE__ */ jsxRuntime.jsx(
3821
3844
  "button",
3822
3845
  {
3823
3846
  type: "button",
3824
3847
  onClick: () => onCtaClick(o),
3825
- "aria-label": `${ctaLabel(o, ctaTextProp, safeFormatCurrency(o.priceCents, o.currency, locale, intlOverride) === "\u2014")} for ${localizedName(o)}`,
3848
+ "aria-label": `${ctaLabel(o, ctaTextProp, tablePrice === "\u2014")} for ${localizedName(o)}`,
3826
3849
  style: {
3827
3850
  inlineSize: "100%",
3828
3851
  padding: "8px 12px",
@@ -3835,7 +3858,7 @@ function TableLayout({
3835
3858
  cursor: "pointer",
3836
3859
  fontFamily: tokens.fontFamily
3837
3860
  },
3838
- children: ctaLabel(o, ctaTextProp, safeFormatCurrency(o.priceCents, o.currency, locale, intlOverride) === "\u2014")
3861
+ children: ctaLabel(o, ctaTextProp, tablePrice === "\u2014")
3839
3862
  }
3840
3863
  )
3841
3864
  ] })