@burdenoff/website-sdk 2026.1014.1 → 2026.1021.2
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.
- package/README.md +22 -4
- package/dist/components/pricing.d.mts +71 -21
- package/dist/components/pricing.d.ts +71 -21
- package/dist/components/pricing.js +331 -34
- package/dist/components/pricing.js.map +1 -1
- package/dist/components/pricing.mjs +327 -36
- package/dist/components/pricing.mjs.map +1 -1
- package/dist/content/index.js +5 -2
- package/dist/content/index.js.map +1 -1
- package/dist/content/index.mjs +5 -2
- package/dist/content/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +317 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +312 -37
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -32,6 +32,168 @@ var __defProp = Object.defineProperty;
|
|
|
32
32
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
33
33
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
34
34
|
|
|
35
|
+
// src/components/pricing-catalog.ts
|
|
36
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
37
|
+
function isUuidLike(value) {
|
|
38
|
+
return UUID_PATTERN.test(value.trim());
|
|
39
|
+
}
|
|
40
|
+
var PricingProductKeyError = class extends Error {
|
|
41
|
+
constructor(productId) {
|
|
42
|
+
super(
|
|
43
|
+
`PricingPage was given the product id "${productId}", which matches no public product. publicPlans(productId) expects the product slug (for example "vibecontrols"); pass product.slug.`
|
|
44
|
+
);
|
|
45
|
+
this.name = "PricingProductKeyError";
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
function resolvePricingProductKey(productId, candidates) {
|
|
49
|
+
const trimmed = productId.trim();
|
|
50
|
+
if (!isUuidLike(trimmed)) return trimmed;
|
|
51
|
+
const wanted = trimmed.toLowerCase();
|
|
52
|
+
const match = candidates.find(
|
|
53
|
+
(c) => !!c && c.id.toLowerCase() === wanted && c.slug.trim().length > 0
|
|
54
|
+
);
|
|
55
|
+
return match ? match.slug.trim() : null;
|
|
56
|
+
}
|
|
57
|
+
var PERIOD_SUFFIX = /\s*[([]\s*(monthly|month|annual|annually|yearly|year)\s*[)\]]\s*$/i;
|
|
58
|
+
var PRODUCT_PREFIX = /^\S.*?\s[-–—]\s+(?=\S)/;
|
|
59
|
+
function cleanPlanDisplayName(name) {
|
|
60
|
+
const withoutPeriod = name.replace(PERIOD_SUFFIX, "").trim();
|
|
61
|
+
const withoutProduct = withoutPeriod.replace(PRODUCT_PREFIX, "").trim();
|
|
62
|
+
if (withoutProduct.length > 0) return withoutProduct;
|
|
63
|
+
if (withoutPeriod.length > 0) return withoutPeriod;
|
|
64
|
+
return name.trim();
|
|
65
|
+
}
|
|
66
|
+
function isFiniteNumber(value) {
|
|
67
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
68
|
+
}
|
|
69
|
+
function planPriceIn(plan, currency) {
|
|
70
|
+
const code = currency.toUpperCase();
|
|
71
|
+
if (plan.price === 0) return 0;
|
|
72
|
+
if (plan.currency.toUpperCase() === code) return plan.price;
|
|
73
|
+
const prices = plan.currencyPrices;
|
|
74
|
+
if (!prices) return null;
|
|
75
|
+
const direct = prices[code];
|
|
76
|
+
if (isFiniteNumber(direct)) return direct;
|
|
77
|
+
const key = Object.keys(prices).find((k) => k.toUpperCase() === code);
|
|
78
|
+
const value = key === void 0 ? void 0 : prices[key];
|
|
79
|
+
return isFiniteNumber(value) ? value : null;
|
|
80
|
+
}
|
|
81
|
+
function availablePlanCurrencies(plans) {
|
|
82
|
+
if (plans.length === 0) return [];
|
|
83
|
+
const all = /* @__PURE__ */ new Set();
|
|
84
|
+
for (const plan of plans) {
|
|
85
|
+
all.add(plan.currency.toUpperCase());
|
|
86
|
+
for (const key of Object.keys(plan.currencyPrices ?? {})) {
|
|
87
|
+
all.add(key.toUpperCase());
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const usable = [...all].filter(
|
|
91
|
+
(code) => plans.every((plan) => planPriceIn(plan, code) !== null)
|
|
92
|
+
);
|
|
93
|
+
const base = plans.find((p) => p.price > 0)?.currency.toUpperCase();
|
|
94
|
+
return usable.sort((a, b) => {
|
|
95
|
+
if (a === base) return -1;
|
|
96
|
+
if (b === base) return 1;
|
|
97
|
+
return a.localeCompare(b);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
var EURO_REGIONS = [
|
|
101
|
+
"AT",
|
|
102
|
+
"BE",
|
|
103
|
+
"CY",
|
|
104
|
+
"DE",
|
|
105
|
+
"EE",
|
|
106
|
+
"ES",
|
|
107
|
+
"FI",
|
|
108
|
+
"FR",
|
|
109
|
+
"GR",
|
|
110
|
+
"HR",
|
|
111
|
+
"IE",
|
|
112
|
+
"IT",
|
|
113
|
+
"LT",
|
|
114
|
+
"LU",
|
|
115
|
+
"LV",
|
|
116
|
+
"MT",
|
|
117
|
+
"NL",
|
|
118
|
+
"PT",
|
|
119
|
+
"SI",
|
|
120
|
+
"SK"
|
|
121
|
+
];
|
|
122
|
+
var REGION_CURRENCY = Object.freeze({
|
|
123
|
+
AU: "AUD",
|
|
124
|
+
BR: "BRL",
|
|
125
|
+
CA: "CAD",
|
|
126
|
+
CH: "CHF",
|
|
127
|
+
CN: "CNY",
|
|
128
|
+
DK: "DKK",
|
|
129
|
+
GB: "GBP",
|
|
130
|
+
HK: "HKD",
|
|
131
|
+
IN: "INR",
|
|
132
|
+
JP: "JPY",
|
|
133
|
+
KR: "KRW",
|
|
134
|
+
MX: "MXN",
|
|
135
|
+
MY: "MYR",
|
|
136
|
+
NO: "NOK",
|
|
137
|
+
NZ: "NZD",
|
|
138
|
+
PL: "PLN",
|
|
139
|
+
SE: "SEK",
|
|
140
|
+
SG: "SGD",
|
|
141
|
+
TH: "THB",
|
|
142
|
+
US: "USD",
|
|
143
|
+
...Object.fromEntries(EURO_REGIONS.map((r) => [r, "EUR"]))
|
|
144
|
+
});
|
|
145
|
+
var TIME_ZONE_REGION = Object.freeze({
|
|
146
|
+
"Asia/Kolkata": "IN",
|
|
147
|
+
"Asia/Calcutta": "IN"
|
|
148
|
+
});
|
|
149
|
+
function regionOfLanguage(tag) {
|
|
150
|
+
const parts = tag.replace(/_/g, "-").split("-");
|
|
151
|
+
for (const part of parts.slice(1)) {
|
|
152
|
+
if (/^[A-Za-z]{2}$/.test(part)) return part.toUpperCase();
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
function detectRegionalCurrency(available, locale) {
|
|
157
|
+
const sellable = new Set(available.map((c) => c.toUpperCase()));
|
|
158
|
+
const regions = [];
|
|
159
|
+
const tzRegion = locale.timeZone ? TIME_ZONE_REGION[locale.timeZone] : null;
|
|
160
|
+
if (tzRegion) regions.push(tzRegion);
|
|
161
|
+
for (const tag of locale.languages ?? []) {
|
|
162
|
+
const region = regionOfLanguage(tag);
|
|
163
|
+
if (region) regions.push(region);
|
|
164
|
+
}
|
|
165
|
+
for (const region of regions) {
|
|
166
|
+
const currency = REGION_CURRENCY[region];
|
|
167
|
+
if (currency && sellable.has(currency)) return currency;
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
function formatCatalogPrice(amount, currency) {
|
|
172
|
+
const code = currency.toUpperCase();
|
|
173
|
+
const whole = amount % 1 === 0;
|
|
174
|
+
try {
|
|
175
|
+
return new Intl.NumberFormat("en-US", {
|
|
176
|
+
style: "currency",
|
|
177
|
+
currency: code,
|
|
178
|
+
minimumFractionDigits: whole ? 0 : 2,
|
|
179
|
+
maximumFractionDigits: whole ? 0 : 2
|
|
180
|
+
}).format(amount);
|
|
181
|
+
} catch {
|
|
182
|
+
return `${code} ${whole ? amount.toString() : amount.toFixed(2)}`;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function plansForBillingCycle(plans, cycle) {
|
|
186
|
+
if (cycle === "monthly") {
|
|
187
|
+
return plans.filter((p) => p.duration === "monthly");
|
|
188
|
+
}
|
|
189
|
+
const yearly = plans.filter((p) => p.duration === "yearly");
|
|
190
|
+
if (yearly.some((p) => p.priceRaw === 0)) return yearly;
|
|
191
|
+
const free = plans.filter(
|
|
192
|
+
(p) => p.duration === "monthly" && p.priceRaw === 0
|
|
193
|
+
);
|
|
194
|
+
return [...free, ...yearly];
|
|
195
|
+
}
|
|
196
|
+
|
|
35
197
|
// src/client/security.ts
|
|
36
198
|
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
37
199
|
var RESERVED_HEADERS = /* @__PURE__ */ new Set([
|
|
@@ -255,6 +417,25 @@ function useWebSDKConfig() {
|
|
|
255
417
|
const client = useWebSDK();
|
|
256
418
|
return client.getConfig();
|
|
257
419
|
}
|
|
420
|
+
|
|
421
|
+
// src/utils/product-slug.ts
|
|
422
|
+
var PRODUCT_SLUG_RENAMES = Object.freeze(
|
|
423
|
+
[Object.freeze({ legacy: "botlit", current: "botmanor" })]
|
|
424
|
+
);
|
|
425
|
+
Object.freeze(
|
|
426
|
+
Object.fromEntries(PRODUCT_SLUG_RENAMES.map((r) => [r.legacy, r.current]))
|
|
427
|
+
);
|
|
428
|
+
var ProductContext = React.createContext({
|
|
429
|
+
product: null,
|
|
430
|
+
loading: true,
|
|
431
|
+
error: null,
|
|
432
|
+
hasProvider: false,
|
|
433
|
+
refetch: () => {
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
function useProduct() {
|
|
437
|
+
return React.useContext(ProductContext);
|
|
438
|
+
}
|
|
258
439
|
function canonicalFromLocation() {
|
|
259
440
|
if (typeof window === "undefined" || !window.location) return void 0;
|
|
260
441
|
const { origin, pathname } = window.location;
|
|
@@ -394,18 +575,49 @@ var GET_PLANS_QUERY = `
|
|
|
394
575
|
}
|
|
395
576
|
}
|
|
396
577
|
`;
|
|
578
|
+
var PUBLIC_PRODUCT_KEYS_QUERY = `
|
|
579
|
+
query PricingProductKeys {
|
|
580
|
+
publicProducts(limit: 200) {
|
|
581
|
+
items {
|
|
582
|
+
id
|
|
583
|
+
slug
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
`;
|
|
588
|
+
var CURRENCY_STORAGE_KEY = "burdenoff.pricing.currency";
|
|
589
|
+
function readStoredCurrency() {
|
|
590
|
+
try {
|
|
591
|
+
if (typeof window === "undefined") return null;
|
|
592
|
+
return window.localStorage.getItem(CURRENCY_STORAGE_KEY);
|
|
593
|
+
} catch {
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function storeCurrency(code) {
|
|
598
|
+
try {
|
|
599
|
+
if (typeof window === "undefined") return;
|
|
600
|
+
window.localStorage.setItem(CURRENCY_STORAGE_KEY, code);
|
|
601
|
+
} catch {
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
function readVisitorLocale() {
|
|
605
|
+
let timeZone = null;
|
|
606
|
+
try {
|
|
607
|
+
timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? null;
|
|
608
|
+
} catch {
|
|
609
|
+
timeZone = null;
|
|
610
|
+
}
|
|
611
|
+
const languages = typeof navigator === "undefined" ? [] : [...navigator.languages ?? [], navigator.language].filter(
|
|
612
|
+
(l) => typeof l === "string" && l.length > 0
|
|
613
|
+
);
|
|
614
|
+
return { timeZone, languages };
|
|
615
|
+
}
|
|
397
616
|
function formatPrice(price, currencyCode = "USD", currencies) {
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
INR: { code: "INR", symbol: "\u20B9", position: "prefix" }
|
|
403
|
-
};
|
|
404
|
-
const currencyMap = currencies || defaultCurrencies;
|
|
405
|
-
const currency = currencyMap[currencyCode] || {
|
|
406
|
-
symbol: currencyCode,
|
|
407
|
-
position: "prefix"
|
|
408
|
-
};
|
|
617
|
+
const currency = currencies?.[currencyCode];
|
|
618
|
+
if (!currency) {
|
|
619
|
+
return formatCatalogPrice(price, currencyCode);
|
|
620
|
+
}
|
|
409
621
|
const formattedNumber = price % 1 === 0 ? price.toString() : price.toFixed(2);
|
|
410
622
|
return currency.position === "prefix" ? `${currency.symbol}${formattedNumber}` : `${formattedNumber}${currency.symbol}`;
|
|
411
623
|
}
|
|
@@ -466,11 +678,12 @@ function getFallbackFeaturesForTier(tier, fallbackFeatures) {
|
|
|
466
678
|
}
|
|
467
679
|
function getMetadataForPlan(planName, priceRaw, planMetadata) {
|
|
468
680
|
const normalizedName = planName.toLowerCase();
|
|
469
|
-
|
|
470
|
-
|
|
681
|
+
const cleanName = cleanPlanDisplayName(planName);
|
|
682
|
+
const meta = planMetadata?.[normalizedName] ?? planMetadata?.[cleanName.toLowerCase()];
|
|
683
|
+
if (meta) {
|
|
471
684
|
return {
|
|
472
685
|
tier: meta.tier || "basic",
|
|
473
|
-
displayName: meta.displayName ||
|
|
686
|
+
displayName: meta.displayName || cleanName,
|
|
474
687
|
defaultDescription: meta.defaultDescription || "Choose this plan",
|
|
475
688
|
popular: meta.popular ?? false,
|
|
476
689
|
ctaText: meta.ctaText || "Get Started",
|
|
@@ -484,7 +697,7 @@ function getMetadataForPlan(planName, priceRaw, planMetadata) {
|
|
|
484
697
|
if (isPaidEnterpriseName) {
|
|
485
698
|
return {
|
|
486
699
|
tier: "professional",
|
|
487
|
-
displayName:
|
|
700
|
+
displayName: cleanName,
|
|
488
701
|
defaultDescription: "Choose this plan",
|
|
489
702
|
popular: false,
|
|
490
703
|
ctaText: "Get Started",
|
|
@@ -493,7 +706,7 @@ function getMetadataForPlan(planName, priceRaw, planMetadata) {
|
|
|
493
706
|
}
|
|
494
707
|
return {
|
|
495
708
|
tier,
|
|
496
|
-
displayName:
|
|
709
|
+
displayName: cleanName,
|
|
497
710
|
defaultDescription: "Choose this plan",
|
|
498
711
|
popular: tier === "professional",
|
|
499
712
|
ctaText: tier === "enterprise" ? "Contact Sales" : "Get Started",
|
|
@@ -502,7 +715,7 @@ function getMetadataForPlan(planName, priceRaw, planMetadata) {
|
|
|
502
715
|
}
|
|
503
716
|
return {
|
|
504
717
|
tier: "basic",
|
|
505
|
-
displayName:
|
|
718
|
+
displayName: cleanName,
|
|
506
719
|
defaultDescription: "Choose this plan",
|
|
507
720
|
popular: false,
|
|
508
721
|
ctaText: "Get Started",
|
|
@@ -559,17 +772,14 @@ function extractDisplayFeatures(features, planName, fallbackFeatures) {
|
|
|
559
772
|
}
|
|
560
773
|
return [];
|
|
561
774
|
}
|
|
562
|
-
function transformToDisplayCard(plan, props) {
|
|
775
|
+
function transformToDisplayCard(plan, props, currency) {
|
|
563
776
|
const metadata = getMetadataForPlan(
|
|
564
777
|
plan.name,
|
|
565
778
|
plan.price,
|
|
566
779
|
props.planMetadata
|
|
567
780
|
);
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
plan.currency,
|
|
571
|
-
props.currencies
|
|
572
|
-
);
|
|
781
|
+
const selectedPrice = currency ? planPriceIn(plan, currency) : null;
|
|
782
|
+
const formattedPrice = currency && selectedPrice !== null ? formatPrice(selectedPrice, currency.toUpperCase(), props.currencies) : formatPrice(plan.price, plan.currency, props.currencies);
|
|
573
783
|
const interval = formatInterval(plan.duration, props.billingIntervals);
|
|
574
784
|
const features = metadata.features && metadata.features.length > 0 ? metadata.features : extractDisplayFeatures(
|
|
575
785
|
plan.features,
|
|
@@ -578,13 +788,14 @@ function transformToDisplayCard(plan, props) {
|
|
|
578
788
|
);
|
|
579
789
|
const contactUrl = props.contactUrl || "/contact";
|
|
580
790
|
const ctaLink = metadata.ctaType === "contact" ? contactUrl : props.appLoginUrl;
|
|
791
|
+
const isQuoteBased = metadata.ctaType === "contact" && plan.price === 0;
|
|
581
792
|
return {
|
|
582
793
|
id: plan.id,
|
|
583
|
-
name:
|
|
794
|
+
name: metadata.displayName,
|
|
584
795
|
description: metadata.defaultDescription,
|
|
585
|
-
price: formattedPrice,
|
|
796
|
+
price: isQuoteBased ? "Custom" : formattedPrice,
|
|
586
797
|
priceRaw: plan.price,
|
|
587
|
-
interval,
|
|
798
|
+
interval: isQuoteBased ? "" : interval,
|
|
588
799
|
duration: plan.duration,
|
|
589
800
|
features,
|
|
590
801
|
popular: metadata.popular,
|
|
@@ -602,8 +813,13 @@ function PricingSection(props) {
|
|
|
602
813
|
} = props;
|
|
603
814
|
const client = useWebSDK();
|
|
604
815
|
const config = useWebSDKConfig();
|
|
605
|
-
const
|
|
606
|
-
const
|
|
816
|
+
const { product: contextProduct } = useProduct();
|
|
817
|
+
const requestedProductId = (props.productId || config.productId || "").trim();
|
|
818
|
+
const contextKey = resolvePricingProductKey(requestedProductId, [
|
|
819
|
+
contextProduct
|
|
820
|
+
]);
|
|
821
|
+
const productKey = contextKey ?? requestedProductId;
|
|
822
|
+
const [catalogPlans, setCatalogPlans] = React.useState([]);
|
|
607
823
|
const [loading, setLoading] = React.useState(true);
|
|
608
824
|
const [error, setError] = React.useState(null);
|
|
609
825
|
const [billingCycle, setBillingCycle] = React.useState(
|
|
@@ -612,13 +828,38 @@ function PricingSection(props) {
|
|
|
612
828
|
const [audience, setAudience] = React.useState(
|
|
613
829
|
"individual"
|
|
614
830
|
);
|
|
831
|
+
const [selectedCurrency, setSelectedCurrency] = React.useState(
|
|
832
|
+
props.defaultCurrency ? props.defaultCurrency.toUpperCase() : null
|
|
833
|
+
);
|
|
615
834
|
React.useEffect(() => {
|
|
616
835
|
let cancelled = false;
|
|
836
|
+
async function resolveKey() {
|
|
837
|
+
if (!productKey) {
|
|
838
|
+
throw new Error(
|
|
839
|
+
"PricingPage needs a product slug: pass productId or set WebSDKConfig.productId."
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
if (!isUuidLike(productKey)) return productKey;
|
|
843
|
+
const response = await client.query(
|
|
844
|
+
PUBLIC_PRODUCT_KEYS_QUERY
|
|
845
|
+
);
|
|
846
|
+
if (response.errors && response.errors.length > 0) {
|
|
847
|
+
throw new Error(response.errors[0].message);
|
|
848
|
+
}
|
|
849
|
+
const resolved = resolvePricingProductKey(
|
|
850
|
+
productKey,
|
|
851
|
+
response.data?.publicProducts?.items ?? []
|
|
852
|
+
);
|
|
853
|
+
if (!resolved) throw new PricingProductKeyError(productKey);
|
|
854
|
+
return resolved;
|
|
855
|
+
}
|
|
617
856
|
async function loadPlans() {
|
|
618
857
|
try {
|
|
619
858
|
setLoading(true);
|
|
859
|
+
const key = await resolveKey();
|
|
860
|
+
if (cancelled) return;
|
|
620
861
|
const response = await client.query(GET_PLANS_QUERY, {
|
|
621
|
-
productId:
|
|
862
|
+
productId: key
|
|
622
863
|
});
|
|
623
864
|
if (cancelled) return;
|
|
624
865
|
if (response.errors && response.errors.length > 0) {
|
|
@@ -627,12 +868,20 @@ function PricingSection(props) {
|
|
|
627
868
|
if (!response.data?.publicPlans) {
|
|
628
869
|
throw new Error("No plans data received");
|
|
629
870
|
}
|
|
630
|
-
const
|
|
631
|
-
|
|
871
|
+
const active = response.data.publicPlans.filter(
|
|
872
|
+
(plan) => plan.isActive
|
|
873
|
+
);
|
|
874
|
+
if (active.length === 0) {
|
|
875
|
+
throw new Error(
|
|
876
|
+
`publicPlans returned no active plans for product "${key}".`
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
setCatalogPlans(active);
|
|
632
880
|
setError(null);
|
|
633
881
|
} catch (err) {
|
|
634
882
|
if (cancelled) return;
|
|
635
883
|
console.error("Failed to load plans:", err);
|
|
884
|
+
setCatalogPlans([]);
|
|
636
885
|
setError("Unable to load pricing information. Please try again later.");
|
|
637
886
|
} finally {
|
|
638
887
|
if (!cancelled) {
|
|
@@ -644,13 +893,38 @@ function PricingSection(props) {
|
|
|
644
893
|
return () => {
|
|
645
894
|
cancelled = true;
|
|
646
895
|
};
|
|
647
|
-
}, [client,
|
|
896
|
+
}, [client, productKey]);
|
|
897
|
+
const currencyOptions = React.useMemo(
|
|
898
|
+
() => availablePlanCurrencies(catalogPlans),
|
|
899
|
+
[catalogPlans]
|
|
900
|
+
);
|
|
901
|
+
React.useEffect(() => {
|
|
902
|
+
if (selectedCurrency || currencyOptions.length < 2) return;
|
|
903
|
+
const stored = readStoredCurrency()?.toUpperCase();
|
|
904
|
+
if (stored && currencyOptions.includes(stored)) {
|
|
905
|
+
setSelectedCurrency(stored);
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
const regional = detectRegionalCurrency(
|
|
909
|
+
currencyOptions,
|
|
910
|
+
readVisitorLocale()
|
|
911
|
+
);
|
|
912
|
+
if (regional) setSelectedCurrency(regional);
|
|
913
|
+
}, [currencyOptions, selectedCurrency]);
|
|
914
|
+
const activeCurrency = selectedCurrency && currencyOptions.includes(selectedCurrency) ? selectedCurrency : currencyOptions[0] ?? null;
|
|
915
|
+
const allPlans = catalogPlans.map((plan) => transformToDisplayCard(plan, props, activeCurrency)).sort((a, b) => {
|
|
916
|
+
const aCustom = a.price === "Custom" ? 1 : 0;
|
|
917
|
+
const bCustom = b.price === "Custom" ? 1 : 0;
|
|
918
|
+
return aCustom - bCustom || a.priceRaw - b.priceRaw;
|
|
919
|
+
});
|
|
920
|
+
const showCurrencySelector = props.showCurrencySelector !== false && currencyOptions.length > 1;
|
|
648
921
|
const {
|
|
649
922
|
individualPlans: individualAllPlans,
|
|
650
923
|
enterprisePlans: enterpriseApiPlans
|
|
651
924
|
} = splitPlansByAudience(allPlans, props.showAudienceTabs);
|
|
652
|
-
const individualPlans =
|
|
653
|
-
|
|
925
|
+
const individualPlans = plansForBillingCycle(
|
|
926
|
+
individualAllPlans,
|
|
927
|
+
billingCycle
|
|
654
928
|
);
|
|
655
929
|
const enterpriseCards = enterpriseApiPlans.map((p) => {
|
|
656
930
|
if (p.priceRaw === 0 && p.name.toLowerCase().includes("enterprise")) {
|
|
@@ -768,6 +1042,23 @@ function PricingSection(props) {
|
|
|
768
1042
|
children: "Yearly"
|
|
769
1043
|
}
|
|
770
1044
|
)
|
|
1045
|
+
] }) }),
|
|
1046
|
+
showCurrencySelector && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center mt-4", children: /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "inline-flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
1047
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Currency" }),
|
|
1048
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1049
|
+
"select",
|
|
1050
|
+
{
|
|
1051
|
+
value: activeCurrency ?? "",
|
|
1052
|
+
onChange: (event) => {
|
|
1053
|
+
const code = event.target.value;
|
|
1054
|
+
setSelectedCurrency(code);
|
|
1055
|
+
storeCurrency(code);
|
|
1056
|
+
},
|
|
1057
|
+
className: "h-9 rounded-md border border-input bg-background px-3 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-ring",
|
|
1058
|
+
"data-testid": "pricing-currency-select",
|
|
1059
|
+
children: currencyOptions.map((code) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: code, children: code }, code))
|
|
1060
|
+
}
|
|
1061
|
+
)
|
|
771
1062
|
] }) })
|
|
772
1063
|
] }),
|
|
773
1064
|
props.showAudienceTabs && audience === "enterprise" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto max-w-3xl text-center mb-8", children: [
|
|
@@ -953,8 +1244,14 @@ function PricingPage(props) {
|
|
|
953
1244
|
}
|
|
954
1245
|
|
|
955
1246
|
exports.PricingPage = PricingPage;
|
|
1247
|
+
exports.PricingProductKeyError = PricingProductKeyError;
|
|
956
1248
|
exports.PricingSection = PricingSection;
|
|
1249
|
+
exports.availablePlanCurrencies = availablePlanCurrencies;
|
|
1250
|
+
exports.cleanPlanDisplayName = cleanPlanDisplayName;
|
|
1251
|
+
exports.isUuidLike = isUuidLike;
|
|
1252
|
+
exports.planPriceIn = planPriceIn;
|
|
957
1253
|
exports.resolveEnterprisePlans = resolveEnterprisePlans;
|
|
1254
|
+
exports.resolvePricingProductKey = resolvePricingProductKey;
|
|
958
1255
|
exports.splitPlansByAudience = splitPlansByAudience;
|
|
959
1256
|
exports.transformFeatureToLabel = transformFeatureToLabel;
|
|
960
1257
|
//# sourceMappingURL=pricing.js.map
|