@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
package/dist/index.mjs
CHANGED
|
@@ -2953,6 +2953,168 @@ function NewsletterForm({
|
|
|
2953
2953
|
)
|
|
2954
2954
|
] });
|
|
2955
2955
|
}
|
|
2956
|
+
|
|
2957
|
+
// src/components/pricing-catalog.ts
|
|
2958
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2959
|
+
function isUuidLike(value) {
|
|
2960
|
+
return UUID_PATTERN.test(value.trim());
|
|
2961
|
+
}
|
|
2962
|
+
var PricingProductKeyError = class extends Error {
|
|
2963
|
+
constructor(productId) {
|
|
2964
|
+
super(
|
|
2965
|
+
`PricingPage was given the product id "${productId}", which matches no public product. publicPlans(productId) expects the product slug (for example "vibecontrols"); pass product.slug.`
|
|
2966
|
+
);
|
|
2967
|
+
this.name = "PricingProductKeyError";
|
|
2968
|
+
}
|
|
2969
|
+
};
|
|
2970
|
+
function resolvePricingProductKey(productId, candidates) {
|
|
2971
|
+
const trimmed = productId.trim();
|
|
2972
|
+
if (!isUuidLike(trimmed)) return trimmed;
|
|
2973
|
+
const wanted = trimmed.toLowerCase();
|
|
2974
|
+
const match = candidates.find(
|
|
2975
|
+
(c) => !!c && c.id.toLowerCase() === wanted && c.slug.trim().length > 0
|
|
2976
|
+
);
|
|
2977
|
+
return match ? match.slug.trim() : null;
|
|
2978
|
+
}
|
|
2979
|
+
var PERIOD_SUFFIX = /\s*[([]\s*(monthly|month|annual|annually|yearly|year)\s*[)\]]\s*$/i;
|
|
2980
|
+
var PRODUCT_PREFIX = /^\S.*?\s[-–—]\s+(?=\S)/;
|
|
2981
|
+
function cleanPlanDisplayName(name) {
|
|
2982
|
+
const withoutPeriod = name.replace(PERIOD_SUFFIX, "").trim();
|
|
2983
|
+
const withoutProduct = withoutPeriod.replace(PRODUCT_PREFIX, "").trim();
|
|
2984
|
+
if (withoutProduct.length > 0) return withoutProduct;
|
|
2985
|
+
if (withoutPeriod.length > 0) return withoutPeriod;
|
|
2986
|
+
return name.trim();
|
|
2987
|
+
}
|
|
2988
|
+
function isFiniteNumber(value) {
|
|
2989
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
2990
|
+
}
|
|
2991
|
+
function planPriceIn(plan, currency) {
|
|
2992
|
+
const code = currency.toUpperCase();
|
|
2993
|
+
if (plan.price === 0) return 0;
|
|
2994
|
+
if (plan.currency.toUpperCase() === code) return plan.price;
|
|
2995
|
+
const prices = plan.currencyPrices;
|
|
2996
|
+
if (!prices) return null;
|
|
2997
|
+
const direct = prices[code];
|
|
2998
|
+
if (isFiniteNumber(direct)) return direct;
|
|
2999
|
+
const key = Object.keys(prices).find((k) => k.toUpperCase() === code);
|
|
3000
|
+
const value = key === void 0 ? void 0 : prices[key];
|
|
3001
|
+
return isFiniteNumber(value) ? value : null;
|
|
3002
|
+
}
|
|
3003
|
+
function availablePlanCurrencies(plans) {
|
|
3004
|
+
if (plans.length === 0) return [];
|
|
3005
|
+
const all = /* @__PURE__ */ new Set();
|
|
3006
|
+
for (const plan of plans) {
|
|
3007
|
+
all.add(plan.currency.toUpperCase());
|
|
3008
|
+
for (const key of Object.keys(plan.currencyPrices ?? {})) {
|
|
3009
|
+
all.add(key.toUpperCase());
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
const usable = [...all].filter(
|
|
3013
|
+
(code) => plans.every((plan) => planPriceIn(plan, code) !== null)
|
|
3014
|
+
);
|
|
3015
|
+
const base = plans.find((p) => p.price > 0)?.currency.toUpperCase();
|
|
3016
|
+
return usable.sort((a, b) => {
|
|
3017
|
+
if (a === base) return -1;
|
|
3018
|
+
if (b === base) return 1;
|
|
3019
|
+
return a.localeCompare(b);
|
|
3020
|
+
});
|
|
3021
|
+
}
|
|
3022
|
+
var EURO_REGIONS = [
|
|
3023
|
+
"AT",
|
|
3024
|
+
"BE",
|
|
3025
|
+
"CY",
|
|
3026
|
+
"DE",
|
|
3027
|
+
"EE",
|
|
3028
|
+
"ES",
|
|
3029
|
+
"FI",
|
|
3030
|
+
"FR",
|
|
3031
|
+
"GR",
|
|
3032
|
+
"HR",
|
|
3033
|
+
"IE",
|
|
3034
|
+
"IT",
|
|
3035
|
+
"LT",
|
|
3036
|
+
"LU",
|
|
3037
|
+
"LV",
|
|
3038
|
+
"MT",
|
|
3039
|
+
"NL",
|
|
3040
|
+
"PT",
|
|
3041
|
+
"SI",
|
|
3042
|
+
"SK"
|
|
3043
|
+
];
|
|
3044
|
+
var REGION_CURRENCY = Object.freeze({
|
|
3045
|
+
AU: "AUD",
|
|
3046
|
+
BR: "BRL",
|
|
3047
|
+
CA: "CAD",
|
|
3048
|
+
CH: "CHF",
|
|
3049
|
+
CN: "CNY",
|
|
3050
|
+
DK: "DKK",
|
|
3051
|
+
GB: "GBP",
|
|
3052
|
+
HK: "HKD",
|
|
3053
|
+
IN: "INR",
|
|
3054
|
+
JP: "JPY",
|
|
3055
|
+
KR: "KRW",
|
|
3056
|
+
MX: "MXN",
|
|
3057
|
+
MY: "MYR",
|
|
3058
|
+
NO: "NOK",
|
|
3059
|
+
NZ: "NZD",
|
|
3060
|
+
PL: "PLN",
|
|
3061
|
+
SE: "SEK",
|
|
3062
|
+
SG: "SGD",
|
|
3063
|
+
TH: "THB",
|
|
3064
|
+
US: "USD",
|
|
3065
|
+
...Object.fromEntries(EURO_REGIONS.map((r) => [r, "EUR"]))
|
|
3066
|
+
});
|
|
3067
|
+
var TIME_ZONE_REGION = Object.freeze({
|
|
3068
|
+
"Asia/Kolkata": "IN",
|
|
3069
|
+
"Asia/Calcutta": "IN"
|
|
3070
|
+
});
|
|
3071
|
+
function regionOfLanguage(tag) {
|
|
3072
|
+
const parts = tag.replace(/_/g, "-").split("-");
|
|
3073
|
+
for (const part of parts.slice(1)) {
|
|
3074
|
+
if (/^[A-Za-z]{2}$/.test(part)) return part.toUpperCase();
|
|
3075
|
+
}
|
|
3076
|
+
return null;
|
|
3077
|
+
}
|
|
3078
|
+
function detectRegionalCurrency(available, locale) {
|
|
3079
|
+
const sellable = new Set(available.map((c) => c.toUpperCase()));
|
|
3080
|
+
const regions = [];
|
|
3081
|
+
const tzRegion = locale.timeZone ? TIME_ZONE_REGION[locale.timeZone] : null;
|
|
3082
|
+
if (tzRegion) regions.push(tzRegion);
|
|
3083
|
+
for (const tag of locale.languages ?? []) {
|
|
3084
|
+
const region = regionOfLanguage(tag);
|
|
3085
|
+
if (region) regions.push(region);
|
|
3086
|
+
}
|
|
3087
|
+
for (const region of regions) {
|
|
3088
|
+
const currency = REGION_CURRENCY[region];
|
|
3089
|
+
if (currency && sellable.has(currency)) return currency;
|
|
3090
|
+
}
|
|
3091
|
+
return null;
|
|
3092
|
+
}
|
|
3093
|
+
function formatCatalogPrice(amount, currency) {
|
|
3094
|
+
const code = currency.toUpperCase();
|
|
3095
|
+
const whole = amount % 1 === 0;
|
|
3096
|
+
try {
|
|
3097
|
+
return new Intl.NumberFormat("en-US", {
|
|
3098
|
+
style: "currency",
|
|
3099
|
+
currency: code,
|
|
3100
|
+
minimumFractionDigits: whole ? 0 : 2,
|
|
3101
|
+
maximumFractionDigits: whole ? 0 : 2
|
|
3102
|
+
}).format(amount);
|
|
3103
|
+
} catch {
|
|
3104
|
+
return `${code} ${whole ? amount.toString() : amount.toFixed(2)}`;
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
function plansForBillingCycle(plans, cycle) {
|
|
3108
|
+
if (cycle === "monthly") {
|
|
3109
|
+
return plans.filter((p) => p.duration === "monthly");
|
|
3110
|
+
}
|
|
3111
|
+
const yearly = plans.filter((p) => p.duration === "yearly");
|
|
3112
|
+
if (yearly.some((p) => p.priceRaw === 0)) return yearly;
|
|
3113
|
+
const free = plans.filter(
|
|
3114
|
+
(p) => p.duration === "monthly" && p.priceRaw === 0
|
|
3115
|
+
);
|
|
3116
|
+
return [...free, ...yearly];
|
|
3117
|
+
}
|
|
2956
3118
|
var GET_PLANS_QUERY = `
|
|
2957
3119
|
query GetPublicPlans($productId: String!) {
|
|
2958
3120
|
publicPlans(productId: $productId) {
|
|
@@ -2978,18 +3140,49 @@ var GET_PLANS_QUERY = `
|
|
|
2978
3140
|
}
|
|
2979
3141
|
}
|
|
2980
3142
|
`;
|
|
3143
|
+
var PUBLIC_PRODUCT_KEYS_QUERY = `
|
|
3144
|
+
query PricingProductKeys {
|
|
3145
|
+
publicProducts(limit: 200) {
|
|
3146
|
+
items {
|
|
3147
|
+
id
|
|
3148
|
+
slug
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
`;
|
|
3153
|
+
var CURRENCY_STORAGE_KEY = "burdenoff.pricing.currency";
|
|
3154
|
+
function readStoredCurrency() {
|
|
3155
|
+
try {
|
|
3156
|
+
if (typeof window === "undefined") return null;
|
|
3157
|
+
return window.localStorage.getItem(CURRENCY_STORAGE_KEY);
|
|
3158
|
+
} catch {
|
|
3159
|
+
return null;
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
function storeCurrency(code) {
|
|
3163
|
+
try {
|
|
3164
|
+
if (typeof window === "undefined") return;
|
|
3165
|
+
window.localStorage.setItem(CURRENCY_STORAGE_KEY, code);
|
|
3166
|
+
} catch {
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
function readVisitorLocale() {
|
|
3170
|
+
let timeZone = null;
|
|
3171
|
+
try {
|
|
3172
|
+
timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? null;
|
|
3173
|
+
} catch {
|
|
3174
|
+
timeZone = null;
|
|
3175
|
+
}
|
|
3176
|
+
const languages = typeof navigator === "undefined" ? [] : [...navigator.languages ?? [], navigator.language].filter(
|
|
3177
|
+
(l) => typeof l === "string" && l.length > 0
|
|
3178
|
+
);
|
|
3179
|
+
return { timeZone, languages };
|
|
3180
|
+
}
|
|
2981
3181
|
function formatPrice(price, currencyCode = "USD", currencies) {
|
|
2982
|
-
const
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
INR: { code: "INR", symbol: "\u20B9", position: "prefix" }
|
|
2987
|
-
};
|
|
2988
|
-
const currencyMap = currencies || defaultCurrencies;
|
|
2989
|
-
const currency = currencyMap[currencyCode] || {
|
|
2990
|
-
symbol: currencyCode,
|
|
2991
|
-
position: "prefix"
|
|
2992
|
-
};
|
|
3182
|
+
const currency = currencies?.[currencyCode];
|
|
3183
|
+
if (!currency) {
|
|
3184
|
+
return formatCatalogPrice(price, currencyCode);
|
|
3185
|
+
}
|
|
2993
3186
|
const formattedNumber = price % 1 === 0 ? price.toString() : price.toFixed(2);
|
|
2994
3187
|
return currency.position === "prefix" ? `${currency.symbol}${formattedNumber}` : `${formattedNumber}${currency.symbol}`;
|
|
2995
3188
|
}
|
|
@@ -3050,11 +3243,12 @@ function getFallbackFeaturesForTier(tier, fallbackFeatures) {
|
|
|
3050
3243
|
}
|
|
3051
3244
|
function getMetadataForPlan(planName, priceRaw, planMetadata) {
|
|
3052
3245
|
const normalizedName = planName.toLowerCase();
|
|
3053
|
-
|
|
3054
|
-
|
|
3246
|
+
const cleanName = cleanPlanDisplayName(planName);
|
|
3247
|
+
const meta = planMetadata?.[normalizedName] ?? planMetadata?.[cleanName.toLowerCase()];
|
|
3248
|
+
if (meta) {
|
|
3055
3249
|
return {
|
|
3056
3250
|
tier: meta.tier || "basic",
|
|
3057
|
-
displayName: meta.displayName ||
|
|
3251
|
+
displayName: meta.displayName || cleanName,
|
|
3058
3252
|
defaultDescription: meta.defaultDescription || "Choose this plan",
|
|
3059
3253
|
popular: meta.popular ?? false,
|
|
3060
3254
|
ctaText: meta.ctaText || "Get Started",
|
|
@@ -3068,7 +3262,7 @@ function getMetadataForPlan(planName, priceRaw, planMetadata) {
|
|
|
3068
3262
|
if (isPaidEnterpriseName) {
|
|
3069
3263
|
return {
|
|
3070
3264
|
tier: "professional",
|
|
3071
|
-
displayName:
|
|
3265
|
+
displayName: cleanName,
|
|
3072
3266
|
defaultDescription: "Choose this plan",
|
|
3073
3267
|
popular: false,
|
|
3074
3268
|
ctaText: "Get Started",
|
|
@@ -3077,7 +3271,7 @@ function getMetadataForPlan(planName, priceRaw, planMetadata) {
|
|
|
3077
3271
|
}
|
|
3078
3272
|
return {
|
|
3079
3273
|
tier,
|
|
3080
|
-
displayName:
|
|
3274
|
+
displayName: cleanName,
|
|
3081
3275
|
defaultDescription: "Choose this plan",
|
|
3082
3276
|
popular: tier === "professional",
|
|
3083
3277
|
ctaText: tier === "enterprise" ? "Contact Sales" : "Get Started",
|
|
@@ -3086,7 +3280,7 @@ function getMetadataForPlan(planName, priceRaw, planMetadata) {
|
|
|
3086
3280
|
}
|
|
3087
3281
|
return {
|
|
3088
3282
|
tier: "basic",
|
|
3089
|
-
displayName:
|
|
3283
|
+
displayName: cleanName,
|
|
3090
3284
|
defaultDescription: "Choose this plan",
|
|
3091
3285
|
popular: false,
|
|
3092
3286
|
ctaText: "Get Started",
|
|
@@ -3143,17 +3337,14 @@ function extractDisplayFeatures(features, planName, fallbackFeatures) {
|
|
|
3143
3337
|
}
|
|
3144
3338
|
return [];
|
|
3145
3339
|
}
|
|
3146
|
-
function transformToDisplayCard(plan, props) {
|
|
3340
|
+
function transformToDisplayCard(plan, props, currency) {
|
|
3147
3341
|
const metadata = getMetadataForPlan(
|
|
3148
3342
|
plan.name,
|
|
3149
3343
|
plan.price,
|
|
3150
3344
|
props.planMetadata
|
|
3151
3345
|
);
|
|
3152
|
-
const
|
|
3153
|
-
|
|
3154
|
-
plan.currency,
|
|
3155
|
-
props.currencies
|
|
3156
|
-
);
|
|
3346
|
+
const selectedPrice = currency ? planPriceIn(plan, currency) : null;
|
|
3347
|
+
const formattedPrice = currency && selectedPrice !== null ? formatPrice(selectedPrice, currency.toUpperCase(), props.currencies) : formatPrice(plan.price, plan.currency, props.currencies);
|
|
3157
3348
|
const interval = formatInterval(plan.duration, props.billingIntervals);
|
|
3158
3349
|
const features = metadata.features && metadata.features.length > 0 ? metadata.features : extractDisplayFeatures(
|
|
3159
3350
|
plan.features,
|
|
@@ -3162,13 +3353,14 @@ function transformToDisplayCard(plan, props) {
|
|
|
3162
3353
|
);
|
|
3163
3354
|
const contactUrl = props.contactUrl || "/contact";
|
|
3164
3355
|
const ctaLink = metadata.ctaType === "contact" ? contactUrl : props.appLoginUrl;
|
|
3356
|
+
const isQuoteBased = metadata.ctaType === "contact" && plan.price === 0;
|
|
3165
3357
|
return {
|
|
3166
3358
|
id: plan.id,
|
|
3167
|
-
name:
|
|
3359
|
+
name: metadata.displayName,
|
|
3168
3360
|
description: metadata.defaultDescription,
|
|
3169
|
-
price: formattedPrice,
|
|
3361
|
+
price: isQuoteBased ? "Custom" : formattedPrice,
|
|
3170
3362
|
priceRaw: plan.price,
|
|
3171
|
-
interval,
|
|
3363
|
+
interval: isQuoteBased ? "" : interval,
|
|
3172
3364
|
duration: plan.duration,
|
|
3173
3365
|
features,
|
|
3174
3366
|
popular: metadata.popular,
|
|
@@ -3186,8 +3378,13 @@ function PricingSection(props) {
|
|
|
3186
3378
|
} = props;
|
|
3187
3379
|
const client = useWebSDK();
|
|
3188
3380
|
const config = useWebSDKConfig();
|
|
3189
|
-
const
|
|
3190
|
-
const
|
|
3381
|
+
const { product: contextProduct } = useProduct();
|
|
3382
|
+
const requestedProductId = (props.productId || config.productId || "").trim();
|
|
3383
|
+
const contextKey = resolvePricingProductKey(requestedProductId, [
|
|
3384
|
+
contextProduct
|
|
3385
|
+
]);
|
|
3386
|
+
const productKey = contextKey ?? requestedProductId;
|
|
3387
|
+
const [catalogPlans, setCatalogPlans] = useState([]);
|
|
3191
3388
|
const [loading, setLoading] = useState(true);
|
|
3192
3389
|
const [error, setError] = useState(null);
|
|
3193
3390
|
const [billingCycle, setBillingCycle] = useState(
|
|
@@ -3196,13 +3393,38 @@ function PricingSection(props) {
|
|
|
3196
3393
|
const [audience, setAudience] = useState(
|
|
3197
3394
|
"individual"
|
|
3198
3395
|
);
|
|
3396
|
+
const [selectedCurrency, setSelectedCurrency] = useState(
|
|
3397
|
+
props.defaultCurrency ? props.defaultCurrency.toUpperCase() : null
|
|
3398
|
+
);
|
|
3199
3399
|
useEffect(() => {
|
|
3200
3400
|
let cancelled = false;
|
|
3401
|
+
async function resolveKey() {
|
|
3402
|
+
if (!productKey) {
|
|
3403
|
+
throw new Error(
|
|
3404
|
+
"PricingPage needs a product slug: pass productId or set WebSDKConfig.productId."
|
|
3405
|
+
);
|
|
3406
|
+
}
|
|
3407
|
+
if (!isUuidLike(productKey)) return productKey;
|
|
3408
|
+
const response = await client.query(
|
|
3409
|
+
PUBLIC_PRODUCT_KEYS_QUERY
|
|
3410
|
+
);
|
|
3411
|
+
if (response.errors && response.errors.length > 0) {
|
|
3412
|
+
throw new Error(response.errors[0].message);
|
|
3413
|
+
}
|
|
3414
|
+
const resolved = resolvePricingProductKey(
|
|
3415
|
+
productKey,
|
|
3416
|
+
response.data?.publicProducts?.items ?? []
|
|
3417
|
+
);
|
|
3418
|
+
if (!resolved) throw new PricingProductKeyError(productKey);
|
|
3419
|
+
return resolved;
|
|
3420
|
+
}
|
|
3201
3421
|
async function loadPlans() {
|
|
3202
3422
|
try {
|
|
3203
3423
|
setLoading(true);
|
|
3424
|
+
const key = await resolveKey();
|
|
3425
|
+
if (cancelled) return;
|
|
3204
3426
|
const response = await client.query(GET_PLANS_QUERY, {
|
|
3205
|
-
productId:
|
|
3427
|
+
productId: key
|
|
3206
3428
|
});
|
|
3207
3429
|
if (cancelled) return;
|
|
3208
3430
|
if (response.errors && response.errors.length > 0) {
|
|
@@ -3211,12 +3433,20 @@ function PricingSection(props) {
|
|
|
3211
3433
|
if (!response.data?.publicPlans) {
|
|
3212
3434
|
throw new Error("No plans data received");
|
|
3213
3435
|
}
|
|
3214
|
-
const
|
|
3215
|
-
|
|
3436
|
+
const active = response.data.publicPlans.filter(
|
|
3437
|
+
(plan) => plan.isActive
|
|
3438
|
+
);
|
|
3439
|
+
if (active.length === 0) {
|
|
3440
|
+
throw new Error(
|
|
3441
|
+
`publicPlans returned no active plans for product "${key}".`
|
|
3442
|
+
);
|
|
3443
|
+
}
|
|
3444
|
+
setCatalogPlans(active);
|
|
3216
3445
|
setError(null);
|
|
3217
3446
|
} catch (err) {
|
|
3218
3447
|
if (cancelled) return;
|
|
3219
3448
|
console.error("Failed to load plans:", err);
|
|
3449
|
+
setCatalogPlans([]);
|
|
3220
3450
|
setError("Unable to load pricing information. Please try again later.");
|
|
3221
3451
|
} finally {
|
|
3222
3452
|
if (!cancelled) {
|
|
@@ -3228,13 +3458,38 @@ function PricingSection(props) {
|
|
|
3228
3458
|
return () => {
|
|
3229
3459
|
cancelled = true;
|
|
3230
3460
|
};
|
|
3231
|
-
}, [client,
|
|
3461
|
+
}, [client, productKey]);
|
|
3462
|
+
const currencyOptions = useMemo(
|
|
3463
|
+
() => availablePlanCurrencies(catalogPlans),
|
|
3464
|
+
[catalogPlans]
|
|
3465
|
+
);
|
|
3466
|
+
useEffect(() => {
|
|
3467
|
+
if (selectedCurrency || currencyOptions.length < 2) return;
|
|
3468
|
+
const stored = readStoredCurrency()?.toUpperCase();
|
|
3469
|
+
if (stored && currencyOptions.includes(stored)) {
|
|
3470
|
+
setSelectedCurrency(stored);
|
|
3471
|
+
return;
|
|
3472
|
+
}
|
|
3473
|
+
const regional = detectRegionalCurrency(
|
|
3474
|
+
currencyOptions,
|
|
3475
|
+
readVisitorLocale()
|
|
3476
|
+
);
|
|
3477
|
+
if (regional) setSelectedCurrency(regional);
|
|
3478
|
+
}, [currencyOptions, selectedCurrency]);
|
|
3479
|
+
const activeCurrency = selectedCurrency && currencyOptions.includes(selectedCurrency) ? selectedCurrency : currencyOptions[0] ?? null;
|
|
3480
|
+
const allPlans = catalogPlans.map((plan) => transformToDisplayCard(plan, props, activeCurrency)).sort((a, b) => {
|
|
3481
|
+
const aCustom = a.price === "Custom" ? 1 : 0;
|
|
3482
|
+
const bCustom = b.price === "Custom" ? 1 : 0;
|
|
3483
|
+
return aCustom - bCustom || a.priceRaw - b.priceRaw;
|
|
3484
|
+
});
|
|
3485
|
+
const showCurrencySelector = props.showCurrencySelector !== false && currencyOptions.length > 1;
|
|
3232
3486
|
const {
|
|
3233
3487
|
individualPlans: individualAllPlans,
|
|
3234
3488
|
enterprisePlans: enterpriseApiPlans
|
|
3235
3489
|
} = splitPlansByAudience(allPlans, props.showAudienceTabs);
|
|
3236
|
-
const individualPlans =
|
|
3237
|
-
|
|
3490
|
+
const individualPlans = plansForBillingCycle(
|
|
3491
|
+
individualAllPlans,
|
|
3492
|
+
billingCycle
|
|
3238
3493
|
);
|
|
3239
3494
|
const enterpriseCards = enterpriseApiPlans.map((p) => {
|
|
3240
3495
|
if (p.priceRaw === 0 && p.name.toLowerCase().includes("enterprise")) {
|
|
@@ -3352,6 +3607,23 @@ function PricingSection(props) {
|
|
|
3352
3607
|
children: "Yearly"
|
|
3353
3608
|
}
|
|
3354
3609
|
)
|
|
3610
|
+
] }) }),
|
|
3611
|
+
showCurrencySelector && /* @__PURE__ */ jsx("div", { className: "flex justify-center mt-4", children: /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
3612
|
+
/* @__PURE__ */ jsx("span", { children: "Currency" }),
|
|
3613
|
+
/* @__PURE__ */ jsx(
|
|
3614
|
+
"select",
|
|
3615
|
+
{
|
|
3616
|
+
value: activeCurrency ?? "",
|
|
3617
|
+
onChange: (event) => {
|
|
3618
|
+
const code = event.target.value;
|
|
3619
|
+
setSelectedCurrency(code);
|
|
3620
|
+
storeCurrency(code);
|
|
3621
|
+
},
|
|
3622
|
+
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",
|
|
3623
|
+
"data-testid": "pricing-currency-select",
|
|
3624
|
+
children: currencyOptions.map((code) => /* @__PURE__ */ jsx("option", { value: code, children: code }, code))
|
|
3625
|
+
}
|
|
3626
|
+
)
|
|
3355
3627
|
] }) })
|
|
3356
3628
|
] }),
|
|
3357
3629
|
props.showAudienceTabs && audience === "enterprise" && /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-3xl text-center mb-8", children: [
|
|
@@ -15045,8 +15317,8 @@ function AnimationStyles() {
|
|
|
15045
15317
|
return /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: ANIMATION_STYLES } });
|
|
15046
15318
|
}
|
|
15047
15319
|
var CASE_STUDIES_QUERY = `
|
|
15048
|
-
query CaseStudies($productId: String!, $limit: Int, $offset: Int) {
|
|
15049
|
-
publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
|
|
15320
|
+
query CaseStudies($productId: String!, $tag: String, $limit: Int, $offset: Int) {
|
|
15321
|
+
publicContentPages(productId: $productId, tag: $tag, limit: $limit, offset: $offset) {
|
|
15050
15322
|
items {
|
|
15051
15323
|
id
|
|
15052
15324
|
title
|
|
@@ -15446,6 +15718,9 @@ function CaseStudiesListPage(props) {
|
|
|
15446
15718
|
try {
|
|
15447
15719
|
const result = await client.query(CASE_STUDIES_QUERY, {
|
|
15448
15720
|
productId: resolvedProductId,
|
|
15721
|
+
// Filter server-side: an unfiltered page of `limit` rows is mostly blog/legal pages, so
|
|
15722
|
+
// real case studies past it were never listed and the page fell back to the empty state.
|
|
15723
|
+
tag: CASE_STUDY_TAG,
|
|
15449
15724
|
limit,
|
|
15450
15725
|
offset
|
|
15451
15726
|
});
|
|
@@ -19038,6 +19313,6 @@ function shouldRenderSiteFooter(pathname) {
|
|
|
19038
19313
|
return !isFooterlessPath(pathname);
|
|
19039
19314
|
}
|
|
19040
19315
|
|
|
19041
|
-
export { ANSWERS_PATH, ANSWER_HEADING_SHIFT, ANSWER_PATH_PREFIX, AnswerDetailPage, AnswersPage, BlogListPage, BlogPostPage, Button, CONCEPTS_PATH, CONCEPT_ILLUSTRATION_NOTICE, CONCEPT_PATH_PREFIX, CONCEPT_WORKFLOW_NOTICE, CareersApplyForm, CareersSubmissionForm, CaseStudiesListPage, CaseStudyPage, ChangelogPage, ConceptDetailPage, ConceptsPage, ContactPage, ContentRenderer, ContractsPage, DEFAULT_CONTACT_CATEGORIES, DEFAULT_EXPLORE_EXAMPLE_PROMPTS, DOMAINS_URL, ECOSYSTEM_PRODUCTS, ESCALATE_EXPLORE_CONVERSATION_MUTATION, EXPLORE_CATALOG_QUERY, EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS, EXPLORE_ESCALATION_NOTE_SOFT_MAX, EXPLORE_LOCALES, EXPLORE_MESSAGE_QUERY, EXPLORE_PREFILL_MAX_LENGTH, EXPLORE_RECAPTCHA_ACTION, ElectronDownloadLink, ExploreAnswerBody, ExploreAnswerStyles, ExploreCta, ExplorePage, FALLBACK_EXPLORE_LIMITS, FOOTERLESS_ROUTES, Input, Label, LaunchCountdown, Markdown, NewsletterForm, NewsletterPage, PRODUCT_SLUG_ALIASES, PRODUCT_SLUG_EMIT, PRODUCT_SLUG_RENAMES, PUBLISHED_ANSWER_FIELDS, PageHead, PartnersPage, PressKitPage, PricingPage, PricingSection, ProblemsSolvedSection, ProductCard, ProductProvider, ResourceLinks, RybbitAnalytics, SEND_EXPLORE_MESSAGE_MUTATION, Select, SocialLinks, StarRating, StoreBrowsePage, StoreHomePage, StoreProductDetailPage, Textarea, TypeBadge, UnsubscribePage, WaitlistForm, WaitlistPage, WebSDKClient, WebSDKProvider, WebsiteSwitcher, answerPath, answerPlainText, answerSourceHost, answerSpeechText, answerStrings, buttonVariants, classifyExploreError, cn, conceptPath, detectElectronPlatform, emitProductSlug, exploreQuestionHref, fetchPublicJobPosting, fetchPublicJobPostings, fetchPublicJobPostingsForSubmission, fetchPublicProduct, formatAnswerDate, formatDownloads, formatPrice2 as formatPrice, getAnswerBySlug, getConceptBySlug, getRecaptchaV3Token, inlineImageOrigins, isFooterlessPath, isInlineImageAllowed, isInlineVideoAllowed, isPublishedSourceUrl, isSameProductSlug, isValidUrl, legacyProductSlug, loadRecaptchaV3, looksLikeDeliverableEmail, normalizeProductSlug, normalizeRoutePath, productSlugVariants, productSlugsByOrigin, shouldRenderSiteFooter, speechLocaleOf, stopSpeaking, useContentPage, useContentPages, useExploreChat, useProduct, useProductConfig, useProgressiveReveal, usePublicJobPosting, usePublicJobPostings, usePublicJobPostingsForSubmission, useSpeechOutput, useWebSDK, useWebSDKConfig };
|
|
19316
|
+
export { ANSWERS_PATH, ANSWER_HEADING_SHIFT, ANSWER_PATH_PREFIX, AnswerDetailPage, AnswersPage, BlogListPage, BlogPostPage, Button, CONCEPTS_PATH, CONCEPT_ILLUSTRATION_NOTICE, CONCEPT_PATH_PREFIX, CONCEPT_WORKFLOW_NOTICE, CareersApplyForm, CareersSubmissionForm, CaseStudiesListPage, CaseStudyPage, ChangelogPage, ConceptDetailPage, ConceptsPage, ContactPage, ContentRenderer, ContractsPage, DEFAULT_CONTACT_CATEGORIES, DEFAULT_EXPLORE_EXAMPLE_PROMPTS, DOMAINS_URL, ECOSYSTEM_PRODUCTS, ESCALATE_EXPLORE_CONVERSATION_MUTATION, EXPLORE_CATALOG_QUERY, EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS, EXPLORE_ESCALATION_NOTE_SOFT_MAX, EXPLORE_LOCALES, EXPLORE_MESSAGE_QUERY, EXPLORE_PREFILL_MAX_LENGTH, EXPLORE_RECAPTCHA_ACTION, ElectronDownloadLink, ExploreAnswerBody, ExploreAnswerStyles, ExploreCta, ExplorePage, FALLBACK_EXPLORE_LIMITS, FOOTERLESS_ROUTES, Input, Label, LaunchCountdown, Markdown, NewsletterForm, NewsletterPage, PRODUCT_SLUG_ALIASES, PRODUCT_SLUG_EMIT, PRODUCT_SLUG_RENAMES, PUBLISHED_ANSWER_FIELDS, PageHead, PartnersPage, PressKitPage, PricingPage, PricingProductKeyError, PricingSection, ProblemsSolvedSection, ProductCard, ProductProvider, ResourceLinks, RybbitAnalytics, SEND_EXPLORE_MESSAGE_MUTATION, Select, SocialLinks, StarRating, StoreBrowsePage, StoreHomePage, StoreProductDetailPage, Textarea, TypeBadge, UnsubscribePage, WaitlistForm, WaitlistPage, WebSDKClient, WebSDKProvider, WebsiteSwitcher, answerPath, answerPlainText, answerSourceHost, answerSpeechText, answerStrings, availablePlanCurrencies, buttonVariants, classifyExploreError, cleanPlanDisplayName, cn, conceptPath, detectElectronPlatform, emitProductSlug, exploreQuestionHref, fetchPublicJobPosting, fetchPublicJobPostings, fetchPublicJobPostingsForSubmission, fetchPublicProduct, formatAnswerDate, formatDownloads, formatPrice2 as formatPrice, getAnswerBySlug, getConceptBySlug, getRecaptchaV3Token, inlineImageOrigins, isFooterlessPath, isInlineImageAllowed, isInlineVideoAllowed, isPublishedSourceUrl, isSameProductSlug, isUuidLike, isValidUrl, legacyProductSlug, loadRecaptchaV3, looksLikeDeliverableEmail, normalizeProductSlug, normalizeRoutePath, planPriceIn, productSlugVariants, productSlugsByOrigin, resolvePricingProductKey, shouldRenderSiteFooter, speechLocaleOf, stopSpeaking, useContentPage, useContentPages, useExploreChat, useProduct, useProductConfig, useProgressiveReveal, usePublicJobPosting, usePublicJobPostings, usePublicJobPostingsForSubmission, useSpeechOutput, useWebSDK, useWebSDKConfig };
|
|
19042
19317
|
//# sourceMappingURL=index.mjs.map
|
|
19043
19318
|
//# sourceMappingURL=index.mjs.map
|