@burdenoff/website-sdk 2026.1031.3 → 2026.1101.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 +42 -8
- package/dist/analytics/index.js +19 -69
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +3 -69
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/avatar-live/index.js +1 -0
- package/dist/avatar-live/index.js.map +1 -1
- package/dist/avatar-live/index.mjs +1 -0
- package/dist/avatar-live/index.mjs.map +1 -1
- package/dist/avatar-live-view/index.js +10 -30
- package/dist/avatar-live-view/index.js.map +1 -1
- package/dist/avatar-live-view/index.mjs +4 -24
- package/dist/avatar-live-view/index.mjs.map +1 -1
- package/dist/chunk-23H4JUL4.js +41 -0
- package/dist/chunk-23H4JUL4.js.map +1 -0
- package/dist/chunk-34RX5P75.mjs +25 -0
- package/dist/chunk-34RX5P75.mjs.map +1 -0
- package/dist/chunk-3LFFY7AZ.js +184 -0
- package/dist/chunk-3LFFY7AZ.js.map +1 -0
- package/dist/chunk-3X26GE4S.js +2794 -0
- package/dist/chunk-3X26GE4S.js.map +1 -0
- package/dist/chunk-45GCA4GG.js +85 -0
- package/dist/chunk-45GCA4GG.js.map +1 -0
- package/dist/chunk-4SAGVLHN.js +876 -0
- package/dist/chunk-4SAGVLHN.js.map +1 -0
- package/dist/chunk-4U6YAII5.js +218 -0
- package/dist/chunk-4U6YAII5.js.map +1 -0
- package/dist/chunk-5GMTBOO6.js +21 -0
- package/dist/chunk-5GMTBOO6.js.map +1 -0
- package/dist/chunk-5JRJ57CL.mjs +329 -0
- package/dist/chunk-5JRJ57CL.mjs.map +1 -0
- package/dist/chunk-6C2DKIC7.js +4 -0
- package/dist/chunk-6C2DKIC7.js.map +1 -0
- package/dist/chunk-6IBFBWC5.mjs +17 -0
- package/dist/chunk-6IBFBWC5.mjs.map +1 -0
- package/dist/chunk-6KY2ZRVI.js +116 -0
- package/dist/chunk-6KY2ZRVI.js.map +1 -0
- package/dist/chunk-6VJT3SM4.mjs +182 -0
- package/dist/chunk-6VJT3SM4.mjs.map +1 -0
- package/dist/chunk-6YZUFMK2.mjs +40 -0
- package/dist/chunk-6YZUFMK2.mjs.map +1 -0
- package/dist/chunk-7A6R7CGG.js +47 -0
- package/dist/chunk-7A6R7CGG.js.map +1 -0
- package/dist/chunk-7PWL4V2R.js +4 -0
- package/dist/chunk-7PWL4V2R.js.map +1 -0
- package/dist/chunk-AA33WDYS.js +161 -0
- package/dist/chunk-AA33WDYS.js.map +1 -0
- package/dist/chunk-AHRGFXV2.js +525 -0
- package/dist/chunk-AHRGFXV2.js.map +1 -0
- package/dist/chunk-B63XLDZH.mjs +109 -0
- package/dist/chunk-B63XLDZH.mjs.map +1 -0
- package/dist/chunk-BX2USEWF.mjs +35 -0
- package/dist/chunk-BX2USEWF.mjs.map +1 -0
- package/dist/chunk-BYSZT35R.js +150 -0
- package/dist/chunk-BYSZT35R.js.map +1 -0
- package/dist/chunk-C6OJIHNO.js +12 -0
- package/dist/chunk-C6OJIHNO.js.map +1 -0
- package/dist/chunk-COOELBX7.mjs +63 -0
- package/dist/chunk-COOELBX7.mjs.map +1 -0
- package/dist/chunk-D2TW7MNC.mjs +2771 -0
- package/dist/chunk-D2TW7MNC.mjs.map +1 -0
- package/dist/chunk-D5KEZ6V6.mjs +3 -0
- package/dist/chunk-D5KEZ6V6.mjs.map +1 -0
- package/dist/chunk-E3GUGJMY.js +46 -0
- package/dist/chunk-E3GUGJMY.js.map +1 -0
- package/dist/chunk-EBZ2HGJ2.mjs +52 -0
- package/dist/chunk-EBZ2HGJ2.mjs.map +1 -0
- package/dist/chunk-EDLTUOIA.mjs +210 -0
- package/dist/chunk-EDLTUOIA.mjs.map +1 -0
- package/dist/chunk-EJQSOT4X.js +4 -0
- package/dist/chunk-EJQSOT4X.js.map +1 -0
- package/dist/chunk-EUPFS6MZ.mjs +2019 -0
- package/dist/chunk-EUPFS6MZ.mjs.map +1 -0
- package/dist/chunk-F3XWDC3B.js +77 -0
- package/dist/chunk-F3XWDC3B.js.map +1 -0
- package/dist/chunk-FFADMVIK.mjs +5127 -0
- package/dist/chunk-FFADMVIK.mjs.map +1 -0
- package/dist/chunk-FJWS7BKT.mjs +3 -0
- package/dist/chunk-FJWS7BKT.mjs.map +1 -0
- package/dist/chunk-FJZJDUOR.mjs +3 -0
- package/dist/chunk-FJZJDUOR.mjs.map +1 -0
- package/dist/chunk-GCFLCO2Z.mjs +145 -0
- package/dist/chunk-GCFLCO2Z.mjs.map +1 -0
- package/dist/chunk-GEXC4LRP.js +61 -0
- package/dist/chunk-GEXC4LRP.js.map +1 -0
- package/dist/chunk-GTRAXQGC.mjs +3 -0
- package/dist/chunk-GTRAXQGC.mjs.map +1 -0
- package/dist/chunk-IKOIRUKW.js +270 -0
- package/dist/chunk-IKOIRUKW.js.map +1 -0
- package/dist/chunk-IXTWOPRN.js +45 -0
- package/dist/chunk-IXTWOPRN.js.map +1 -0
- package/dist/chunk-JDXOXGOK.js +547 -0
- package/dist/chunk-JDXOXGOK.js.map +1 -0
- package/dist/chunk-JSLDWYV7.mjs +558 -0
- package/dist/chunk-JSLDWYV7.mjs.map +1 -0
- package/dist/chunk-JXLSTVBK.js +336 -0
- package/dist/chunk-JXLSTVBK.js.map +1 -0
- package/dist/chunk-KHYXK7FG.mjs +49 -0
- package/dist/chunk-KHYXK7FG.mjs.map +1 -0
- package/dist/chunk-KSMBOWQQ.js +2034 -0
- package/dist/chunk-KSMBOWQQ.js.map +1 -0
- package/dist/chunk-KU2PXHGB.mjs +83 -0
- package/dist/chunk-KU2PXHGB.mjs.map +1 -0
- package/dist/chunk-KZANIV3S.mjs +37 -0
- package/dist/chunk-KZANIV3S.mjs.map +1 -0
- package/dist/chunk-L4BP6YOB.js +20 -0
- package/dist/chunk-L4BP6YOB.js.map +1 -0
- package/dist/chunk-MRKA2OAF.mjs +75 -0
- package/dist/chunk-MRKA2OAF.mjs.map +1 -0
- package/dist/chunk-N5EC53MH.mjs +80 -0
- package/dist/chunk-N5EC53MH.mjs.map +1 -0
- package/dist/chunk-NZANQGDM.mjs +34 -0
- package/dist/chunk-NZANQGDM.mjs.map +1 -0
- package/dist/chunk-O4GS3XPZ.js +10 -0
- package/dist/chunk-O4GS3XPZ.js.map +1 -0
- package/dist/chunk-O76QDGRM.js +5154 -0
- package/dist/chunk-O76QDGRM.js.map +1 -0
- package/dist/chunk-OYEUNZJY.js +114 -0
- package/dist/chunk-OYEUNZJY.js.map +1 -0
- package/dist/chunk-PHXAGQNB.js +74 -0
- package/dist/chunk-PHXAGQNB.js.map +1 -0
- package/dist/chunk-PIHHPP7J.mjs +16 -0
- package/dist/chunk-PIHHPP7J.mjs.map +1 -0
- package/dist/chunk-PXRHVVOU.mjs +499 -0
- package/dist/chunk-PXRHVVOU.mjs.map +1 -0
- package/dist/chunk-QMA6U6AU.js +36 -0
- package/dist/chunk-QMA6U6AU.js.map +1 -0
- package/dist/chunk-R6J7IULF.js +44 -0
- package/dist/chunk-R6J7IULF.js.map +1 -0
- package/dist/chunk-R7G6TWIS.mjs +10 -0
- package/dist/chunk-R7G6TWIS.mjs.map +1 -0
- package/dist/chunk-RUMGFI6L.mjs +544 -0
- package/dist/chunk-RUMGFI6L.mjs.map +1 -0
- package/dist/chunk-S54JWSJF.js +565 -0
- package/dist/chunk-S54JWSJF.js.map +1 -0
- package/dist/chunk-SK5UGG6A.js +2041 -0
- package/dist/chunk-SK5UGG6A.js.map +1 -0
- package/dist/chunk-SKIYDKIS.mjs +70 -0
- package/dist/chunk-SKIYDKIS.mjs.map +1 -0
- package/dist/chunk-SZNS5IK6.mjs +322 -0
- package/dist/chunk-SZNS5IK6.mjs.map +1 -0
- package/dist/chunk-T7JIPZAC.js +66 -0
- package/dist/chunk-T7JIPZAC.js.map +1 -0
- package/dist/chunk-TGYQHO2E.mjs +864 -0
- package/dist/chunk-TGYQHO2E.mjs.map +1 -0
- package/dist/chunk-TT3MHN42.mjs +8 -0
- package/dist/chunk-TT3MHN42.mjs.map +1 -0
- package/dist/chunk-UM55U65W.mjs +268 -0
- package/dist/chunk-UM55U65W.mjs.map +1 -0
- package/dist/chunk-UQ2HALUD.js +72 -0
- package/dist/chunk-UQ2HALUD.js.map +1 -0
- package/dist/chunk-UXWT7G77.js +82 -0
- package/dist/chunk-UXWT7G77.js.map +1 -0
- package/dist/chunk-VNS5WDWO.mjs +7 -0
- package/dist/chunk-VNS5WDWO.mjs.map +1 -0
- package/dist/chunk-VRWZLGR7.js +4 -0
- package/dist/chunk-VRWZLGR7.js.map +1 -0
- package/dist/chunk-VZLDSNH4.mjs +22 -0
- package/dist/chunk-VZLDSNH4.mjs.map +1 -0
- package/dist/chunk-W6P3YETJ.js +92 -0
- package/dist/chunk-W6P3YETJ.js.map +1 -0
- package/dist/chunk-WPKFFQCR.mjs +90 -0
- package/dist/chunk-WPKFFQCR.mjs.map +1 -0
- package/dist/chunk-WTVTI5TO.mjs +22 -0
- package/dist/chunk-WTVTI5TO.mjs.map +1 -0
- package/dist/chunk-XJOBZ4RJ.js +44 -0
- package/dist/chunk-XJOBZ4RJ.js.map +1 -0
- package/dist/chunk-XP2JDNDU.js +325 -0
- package/dist/chunk-XP2JDNDU.js.map +1 -0
- package/dist/chunk-Y4V5CEWD.mjs +112 -0
- package/dist/chunk-Y4V5CEWD.mjs.map +1 -0
- package/dist/chunk-YERN2UWM.js +116 -0
- package/dist/chunk-YERN2UWM.js.map +1 -0
- package/dist/chunk-ZKJQWFAA.mjs +114 -0
- package/dist/chunk-ZKJQWFAA.mjs.map +1 -0
- package/dist/chunk-ZOON3S3Y.mjs +138 -0
- package/dist/chunk-ZOON3S3Y.mjs.map +1 -0
- package/dist/chunk-ZP6QRAYC.mjs +2024 -0
- package/dist/chunk-ZP6QRAYC.mjs.map +1 -0
- package/dist/chunk-ZSTFX7FV.js +9 -0
- package/dist/chunk-ZSTFX7FV.js.map +1 -0
- package/dist/client/index.js +23 -331
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +3 -330
- package/dist/client/index.mjs.map +1 -1
- package/dist/comparison/index.js +28 -111
- package/dist/comparison/index.js.map +1 -1
- package/dist/comparison/index.mjs +2 -107
- package/dist/comparison/index.mjs.map +1 -1
- package/dist/components/contact.js +21 -931
- package/dist/components/contact.js.map +1 -1
- package/dist/components/contact.mjs +12 -909
- package/dist/components/contact.mjs.map +1 -1
- package/dist/components/electron-download.js +12 -124
- package/dist/components/electron-download.js.map +1 -1
- package/dist/components/electron-download.mjs +4 -106
- package/dist/components/electron-download.mjs.map +1 -1
- package/dist/components/explore-cta.js +8 -112
- package/dist/components/explore-cta.js.map +1 -1
- package/dist/components/explore-cta.mjs +3 -114
- package/dist/components/explore-cta.mjs.map +1 -1
- package/dist/components/explore.js +62 -7833
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +13 -7800
- package/dist/components/explore.mjs.map +1 -1
- package/dist/components/launch-countdown.js +8 -83
- package/dist/components/launch-countdown.js.map +1 -1
- package/dist/components/launch-countdown.mjs +3 -85
- package/dist/components/launch-countdown.mjs.map +1 -1
- package/dist/components/newsletter.js +20 -993
- package/dist/components/newsletter.js.map +1 -1
- package/dist/components/newsletter.mjs +11 -971
- package/dist/components/newsletter.mjs.map +1 -1
- package/dist/components/partners.js +24 -1302
- package/dist/components/partners.js.map +1 -1
- package/dist/components/partners.mjs +15 -1276
- package/dist/components/partners.mjs.map +1 -1
- package/dist/components/pricing.js +49 -1249
- package/dist/components/pricing.js.map +1 -1
- package/dist/components/pricing.mjs +8 -1226
- package/dist/components/pricing.mjs.map +1 -1
- package/dist/components/problems-solved.js +8 -150
- package/dist/components/problems-solved.js.map +1 -1
- package/dist/components/problems-solved.mjs +4 -133
- package/dist/components/problems-solved.mjs.map +1 -1
- package/dist/components/resource-links.js +8 -304
- package/dist/components/resource-links.js.map +1 -1
- package/dist/components/resource-links.mjs +5 -308
- package/dist/components/resource-links.mjs.map +1 -1
- package/dist/components/social-links.js +8 -343
- package/dist/components/social-links.js.map +1 -1
- package/dist/components/social-links.mjs +5 -347
- package/dist/components/social-links.mjs.map +1 -1
- package/dist/components/unsubscribe.js +10 -627
- package/dist/components/unsubscribe.js.map +1 -1
- package/dist/components/unsubscribe.mjs +6 -611
- package/dist/components/unsubscribe.mjs.map +1 -1
- package/dist/components/waitlist.js +21 -1001
- package/dist/components/waitlist.js.map +1 -1
- package/dist/components/waitlist.mjs +12 -982
- package/dist/components/waitlist.mjs.map +1 -1
- package/dist/components/website-switcher.js +12 -175
- package/dist/components/website-switcher.js.map +1 -1
- package/dist/components/website-switcher.mjs +4 -157
- package/dist/components/website-switcher.mjs.map +1 -1
- package/dist/content/index.js +118 -3616
- package/dist/content/index.js.map +1 -1
- package/dist/content/index.mjs +12 -3567
- package/dist/content/index.mjs.map +1 -1
- package/dist/hooks/index.js +37 -2321
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +6 -2318
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.js +4145 -20599
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3766 -20567
- package/dist/index.mjs.map +1 -1
- package/dist/product/index.js +20 -393
- package/dist/product/index.js.map +1 -1
- package/dist/product/index.mjs +5 -394
- package/dist/product/index.mjs.map +1 -1
- package/dist/seo/index.js +8 -76
- package/dist/seo/index.js.map +1 -1
- package/dist/seo/index.mjs +3 -78
- package/dist/seo/index.mjs.map +1 -1
- package/dist/store/index.js +51 -2650
- package/dist/store/index.js.map +1 -1
- package/dist/store/index.mjs +13 -2619
- package/dist/store/index.mjs.map +1 -1
- package/dist/utils/index.js +69 -134
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs +5 -122
- package/dist/utils/index.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -1,1259 +1,59 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
var chunk4SAGVLHN_js = require('../chunk-4SAGVLHN.js');
|
|
4
|
+
require('../chunk-UXWT7G77.js');
|
|
5
|
+
require('../chunk-UQ2HALUD.js');
|
|
6
|
+
require('../chunk-C6OJIHNO.js');
|
|
7
|
+
require('../chunk-BYSZT35R.js');
|
|
8
|
+
require('../chunk-E3GUGJMY.js');
|
|
9
|
+
require('../chunk-JXLSTVBK.js');
|
|
10
|
+
require('../chunk-ZSTFX7FV.js');
|
|
10
11
|
|
|
11
|
-
function _interopNamespace(e) {
|
|
12
|
-
if (e && e.__esModule) return e;
|
|
13
|
-
var n = Object.create(null);
|
|
14
|
-
if (e) {
|
|
15
|
-
Object.keys(e).forEach(function (k) {
|
|
16
|
-
if (k !== 'default') {
|
|
17
|
-
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
18
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
19
|
-
enumerable: true,
|
|
20
|
-
get: function () { return e[k]; }
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
n.default = e;
|
|
26
|
-
return Object.freeze(n);
|
|
27
|
-
}
|
|
28
12
|
|
|
29
|
-
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
30
13
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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"]))
|
|
14
|
+
Object.defineProperty(exports, "PricingPage", {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: function () { return chunk4SAGVLHN_js.PricingPage; }
|
|
144
17
|
});
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
18
|
+
Object.defineProperty(exports, "PricingProductKeyError", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () { return chunk4SAGVLHN_js.PricingProductKeyError; }
|
|
148
21
|
});
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
function
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
197
|
-
// src/client/security.ts
|
|
198
|
-
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
199
|
-
var RESERVED_HEADERS = /* @__PURE__ */ new Set([
|
|
200
|
-
"authorization",
|
|
201
|
-
"cookie",
|
|
202
|
-
"x-actor-id",
|
|
203
|
-
"x-actor-type",
|
|
204
|
-
"x-consent-granted-by",
|
|
205
|
-
"x-consent-scope",
|
|
206
|
-
"x-consent-granted-at",
|
|
207
|
-
"x-consent-expires-at"
|
|
208
|
-
]);
|
|
209
|
-
function validateGatewayUrl(value) {
|
|
210
|
-
let url;
|
|
211
|
-
try {
|
|
212
|
-
url = new URL(value);
|
|
213
|
-
} catch {
|
|
214
|
-
throw new Error("Gateway URL must be a valid absolute URL");
|
|
215
|
-
}
|
|
216
|
-
if (url.protocol === "https:") {
|
|
217
|
-
return url.toString();
|
|
218
|
-
}
|
|
219
|
-
if (url.protocol === "http:" && LOOPBACK_HOSTS.has(url.hostname.toLowerCase())) {
|
|
220
|
-
return url.toString();
|
|
221
|
-
}
|
|
222
|
-
throw new Error("Gateway URL must use HTTPS unless it targets localhost");
|
|
223
|
-
}
|
|
224
|
-
function sanitizeHeaders(headers) {
|
|
225
|
-
if (!headers) return void 0;
|
|
226
|
-
return Object.fromEntries(
|
|
227
|
-
Object.entries(headers).filter(
|
|
228
|
-
([key]) => !RESERVED_HEADERS.has(key.toLowerCase())
|
|
229
|
-
)
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
var CF_ACCESS_GATED_HOSTS = /* @__PURE__ */ new Set([
|
|
233
|
-
"alphagraphql.burdenoff.com",
|
|
234
|
-
"alphagraphqlworkspaces.burdenoff.com"
|
|
235
|
-
]);
|
|
236
|
-
function cfAccessHeaders(gatewayUrl, cfAccess) {
|
|
237
|
-
const clientId = cfAccess?.clientId?.trim();
|
|
238
|
-
const clientSecret = cfAccess?.clientSecret?.trim();
|
|
239
|
-
if (!clientId || !clientSecret) return {};
|
|
240
|
-
let host;
|
|
241
|
-
try {
|
|
242
|
-
host = new URL(gatewayUrl).hostname.toLowerCase();
|
|
243
|
-
} catch {
|
|
244
|
-
return {};
|
|
245
|
-
}
|
|
246
|
-
if (!CF_ACCESS_GATED_HOSTS.has(host)) return {};
|
|
247
|
-
return {
|
|
248
|
-
"CF-Access-Client-Id": clientId,
|
|
249
|
-
"CF-Access-Client-Secret": clientSecret
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// src/client/graphql-client.ts
|
|
254
|
-
var _WebSDKClient = class _WebSDKClient {
|
|
255
|
-
constructor(config) {
|
|
256
|
-
__publicField(this, "config");
|
|
257
|
-
__publicField(this, "queryCache", /* @__PURE__ */ new Map());
|
|
258
|
-
__publicField(this, "inflight", /* @__PURE__ */ new Map());
|
|
259
|
-
this.config = {
|
|
260
|
-
...config,
|
|
261
|
-
gatewayUrl: validateGatewayUrl(config.gatewayUrl),
|
|
262
|
-
headers: sanitizeHeaders(config.headers)
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
getConfig() {
|
|
266
|
-
return this.config;
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* Execute a GraphQL query (with deduplication and caching).
|
|
270
|
-
*
|
|
271
|
-
* Pass `{ cache: false }` to bypass the cache entirely — required for poll
|
|
272
|
-
* loops, where every tick must reach the network.
|
|
273
|
-
*/
|
|
274
|
-
async query(query, variables, options) {
|
|
275
|
-
if (options?.cache === false) {
|
|
276
|
-
return this.request(query, variables, true, options.timeoutMs);
|
|
277
|
-
}
|
|
278
|
-
const cacheKey = JSON.stringify({ query, variables });
|
|
279
|
-
const cached = this.queryCache.get(cacheKey);
|
|
280
|
-
if (cached) {
|
|
281
|
-
if (Date.now() - cached.ts < _WebSDKClient.CACHE_TTL) {
|
|
282
|
-
return cached.data;
|
|
283
|
-
}
|
|
284
|
-
this.queryCache.delete(cacheKey);
|
|
285
|
-
}
|
|
286
|
-
const existing = this.inflight.get(cacheKey);
|
|
287
|
-
if (existing) {
|
|
288
|
-
return existing;
|
|
289
|
-
}
|
|
290
|
-
const promise = this.request(query, variables, true, options?.timeoutMs).then((result) => {
|
|
291
|
-
if (result.data && !result.errors?.length) {
|
|
292
|
-
if (this.queryCache.size >= _WebSDKClient.MAX_CACHE_ENTRIES) {
|
|
293
|
-
const oldestEntry = this.queryCache.keys().next().value;
|
|
294
|
-
if (oldestEntry) {
|
|
295
|
-
this.queryCache.delete(oldestEntry);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
this.queryCache.set(cacheKey, { data: result, ts: Date.now() });
|
|
299
|
-
}
|
|
300
|
-
return result;
|
|
301
|
-
}).finally(() => {
|
|
302
|
-
this.inflight.delete(cacheKey);
|
|
303
|
-
});
|
|
304
|
-
this.inflight.set(cacheKey, promise);
|
|
305
|
-
return promise;
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Execute a GraphQL mutation
|
|
309
|
-
*/
|
|
310
|
-
async mutate(mutation, variables, options) {
|
|
311
|
-
return this.request(mutation, variables, false, options?.timeoutMs);
|
|
312
|
-
}
|
|
313
|
-
/** HTTP statuses worth another attempt — transient server/edge conditions. */
|
|
314
|
-
static isRetriableStatus(status) {
|
|
315
|
-
return status === 429 || status === 408 || status >= 500 && status < 600;
|
|
316
|
-
}
|
|
317
|
-
static sleep(ms) {
|
|
318
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
319
|
-
}
|
|
320
|
-
/**
|
|
321
|
-
* Perform the HTTP call with a hard timeout. Every outcome resolves to a
|
|
322
|
-
* GraphQLResponse — callers never hang, so a UI's `loading` state always
|
|
323
|
-
* settles into data or a renderable error.
|
|
324
|
-
*/
|
|
325
|
-
async attempt(query, variables, timeoutMs) {
|
|
326
|
-
const controller = new AbortController();
|
|
327
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
328
|
-
try {
|
|
329
|
-
const response = await fetch(this.config.gatewayUrl, {
|
|
330
|
-
method: "POST",
|
|
331
|
-
headers: {
|
|
332
|
-
"Content-Type": "application/json",
|
|
333
|
-
"x-product-id": this.config.productId,
|
|
334
|
-
...this.config.headers,
|
|
335
|
-
...cfAccessHeaders(this.config.gatewayUrl, this.config.cfAccess)
|
|
336
|
-
},
|
|
337
|
-
body: JSON.stringify({ query, variables }),
|
|
338
|
-
signal: controller.signal
|
|
339
|
-
});
|
|
340
|
-
if (!response.ok) {
|
|
341
|
-
console.error(
|
|
342
|
-
"[WebSDK] GraphQL request failed",
|
|
343
|
-
response.status,
|
|
344
|
-
response.statusText
|
|
345
|
-
);
|
|
346
|
-
return {
|
|
347
|
-
res: {
|
|
348
|
-
errors: [
|
|
349
|
-
{
|
|
350
|
-
message: `HTTP ${response.status}: ${response.statusText}`,
|
|
351
|
-
extensions: { code: `HTTP_${response.status}` }
|
|
352
|
-
}
|
|
353
|
-
]
|
|
354
|
-
},
|
|
355
|
-
retriable: _WebSDKClient.isRetriableStatus(response.status)
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
return {
|
|
359
|
-
res: await response.json(),
|
|
360
|
-
retriable: false
|
|
361
|
-
};
|
|
362
|
-
} catch (error) {
|
|
363
|
-
const aborted = error instanceof Error && (error.name === "AbortError" || controller.signal.aborted);
|
|
364
|
-
const message = aborted ? `Request timed out after ${timeoutMs}ms` : error instanceof Error ? error.message : "Network error";
|
|
365
|
-
console.error("[WebSDK] Network error", message);
|
|
366
|
-
return {
|
|
367
|
-
res: {
|
|
368
|
-
errors: [
|
|
369
|
-
{
|
|
370
|
-
message,
|
|
371
|
-
extensions: { code: aborted ? "TIMEOUT" : "NETWORK_ERROR" }
|
|
372
|
-
}
|
|
373
|
-
]
|
|
374
|
-
},
|
|
375
|
-
// Transport-level failures are worth one more try; a genuine outage
|
|
376
|
-
// just fails twice quickly and still renders an error state.
|
|
377
|
-
retriable: true
|
|
378
|
-
};
|
|
379
|
-
} finally {
|
|
380
|
-
clearTimeout(timer);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
async request(query, variables, retry = true, timeoutOverrideMs) {
|
|
384
|
-
const timeoutMs = timeoutOverrideMs ?? this.config.timeoutMs ?? _WebSDKClient.DEFAULT_TIMEOUT_MS;
|
|
385
|
-
const maxRetries = retry ? this.config.maxRetries ?? _WebSDKClient.DEFAULT_MAX_RETRIES : 0;
|
|
386
|
-
let last;
|
|
387
|
-
for (let i = 0; i <= maxRetries; i++) {
|
|
388
|
-
const { res, retriable } = await this.attempt(
|
|
389
|
-
query,
|
|
390
|
-
variables,
|
|
391
|
-
timeoutMs
|
|
392
|
-
);
|
|
393
|
-
if (!retriable || i === maxRetries) return res;
|
|
394
|
-
last = res;
|
|
395
|
-
await _WebSDKClient.sleep(
|
|
396
|
-
_WebSDKClient.RETRY_BASE_DELAY_MS * (i + 1) + Math.random() * 200
|
|
397
|
-
);
|
|
398
|
-
}
|
|
399
|
-
return last;
|
|
400
|
-
}
|
|
401
|
-
};
|
|
402
|
-
__publicField(_WebSDKClient, "CACHE_TTL", 5 * 60 * 1e3);
|
|
403
|
-
// 5 minutes
|
|
404
|
-
__publicField(_WebSDKClient, "MAX_CACHE_ENTRIES", 100);
|
|
405
|
-
__publicField(_WebSDKClient, "DEFAULT_TIMEOUT_MS", 12e3);
|
|
406
|
-
__publicField(_WebSDKClient, "DEFAULT_MAX_RETRIES", 1);
|
|
407
|
-
__publicField(_WebSDKClient, "RETRY_BASE_DELAY_MS", 400);
|
|
408
|
-
var WebSDKContext = React.createContext(null);
|
|
409
|
-
function useWebSDK() {
|
|
410
|
-
const client = React.useContext(WebSDKContext);
|
|
411
|
-
if (!client) {
|
|
412
|
-
throw new Error("useWebSDK must be used within a WebSDKProvider");
|
|
413
|
-
}
|
|
414
|
-
return client;
|
|
415
|
-
}
|
|
416
|
-
function useWebSDKConfig() {
|
|
417
|
-
const client = useWebSDK();
|
|
418
|
-
return client.getConfig();
|
|
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
|
-
}
|
|
22
|
+
Object.defineProperty(exports, "PricingSection", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
get: function () { return chunk4SAGVLHN_js.PricingSection; }
|
|
25
|
+
});
|
|
26
|
+
Object.defineProperty(exports, "availablePlanCurrencies", {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
get: function () { return chunk4SAGVLHN_js.availablePlanCurrencies; }
|
|
29
|
+
});
|
|
30
|
+
Object.defineProperty(exports, "cleanPlanDisplayName", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
get: function () { return chunk4SAGVLHN_js.cleanPlanDisplayName; }
|
|
33
|
+
});
|
|
34
|
+
Object.defineProperty(exports, "isUuidLike", {
|
|
35
|
+
enumerable: true,
|
|
36
|
+
get: function () { return chunk4SAGVLHN_js.isUuidLike; }
|
|
37
|
+
});
|
|
38
|
+
Object.defineProperty(exports, "planPriceIn", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
get: function () { return chunk4SAGVLHN_js.planPriceIn; }
|
|
41
|
+
});
|
|
42
|
+
Object.defineProperty(exports, "resolveEnterprisePlans", {
|
|
43
|
+
enumerable: true,
|
|
44
|
+
get: function () { return chunk4SAGVLHN_js.resolveEnterprisePlans; }
|
|
45
|
+
});
|
|
46
|
+
Object.defineProperty(exports, "resolvePricingProductKey", {
|
|
47
|
+
enumerable: true,
|
|
48
|
+
get: function () { return chunk4SAGVLHN_js.resolvePricingProductKey; }
|
|
49
|
+
});
|
|
50
|
+
Object.defineProperty(exports, "splitPlansByAudience", {
|
|
51
|
+
enumerable: true,
|
|
52
|
+
get: function () { return chunk4SAGVLHN_js.splitPlansByAudience; }
|
|
53
|
+
});
|
|
54
|
+
Object.defineProperty(exports, "transformFeatureToLabel", {
|
|
55
|
+
enumerable: true,
|
|
56
|
+
get: function () { return chunk4SAGVLHN_js.transformFeatureToLabel; }
|
|
435
57
|
});
|
|
436
|
-
function useProduct() {
|
|
437
|
-
return React.useContext(ProductContext);
|
|
438
|
-
}
|
|
439
|
-
function canonicalFromLocation() {
|
|
440
|
-
if (typeof window === "undefined" || !window.location) return void 0;
|
|
441
|
-
const { origin, pathname } = window.location;
|
|
442
|
-
if (!origin || origin === "null") return void 0;
|
|
443
|
-
const path = pathname === "/" ? "/" : pathname.replace(/\/+$/, "");
|
|
444
|
-
return `${origin}${path}`;
|
|
445
|
-
}
|
|
446
|
-
function resolveImageUrl(image, base) {
|
|
447
|
-
if (!image || /^https?:\/\//i.test(image)) return image;
|
|
448
|
-
if (!base) return image;
|
|
449
|
-
try {
|
|
450
|
-
return new URL(image, base).toString();
|
|
451
|
-
} catch {
|
|
452
|
-
return image;
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
function serializeJsonLd(data) {
|
|
456
|
-
return JSON.stringify(data).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
457
|
-
}
|
|
458
|
-
function PageHead({
|
|
459
|
-
title,
|
|
460
|
-
description,
|
|
461
|
-
productName,
|
|
462
|
-
keywords,
|
|
463
|
-
image,
|
|
464
|
-
url,
|
|
465
|
-
favicon,
|
|
466
|
-
robots = "index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1",
|
|
467
|
-
locale = "en_US",
|
|
468
|
-
twitterHandle,
|
|
469
|
-
themeColor = "#000000",
|
|
470
|
-
author,
|
|
471
|
-
structuredData,
|
|
472
|
-
additionalMeta
|
|
473
|
-
}) {
|
|
474
|
-
const fullTitle = productName && !title.includes(productName) ? `${title} | ${productName}` : title;
|
|
475
|
-
const resolvedUrl = url ?? canonicalFromLocation();
|
|
476
|
-
const fullImageUrl = resolveImageUrl(image, resolvedUrl);
|
|
477
|
-
const structuredDataItems = structuredData ? Array.isArray(structuredData) ? structuredData : [structuredData] : [];
|
|
478
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(reactHelmetAsync.Helmet, { children: [
|
|
479
|
-
/* @__PURE__ */ jsxRuntime.jsx("title", { children: fullTitle }),
|
|
480
|
-
/* @__PURE__ */ jsxRuntime.jsx("meta", { name: "title", content: fullTitle }),
|
|
481
|
-
description && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "description", content: description }),
|
|
482
|
-
keywords && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "keywords", content: keywords }),
|
|
483
|
-
author && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "author", content: author }),
|
|
484
|
-
/* @__PURE__ */ jsxRuntime.jsx("meta", { name: "robots", content: robots }),
|
|
485
|
-
resolvedUrl && /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "canonical", href: resolvedUrl }),
|
|
486
|
-
favicon && /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "icon", href: favicon }),
|
|
487
|
-
/* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:type", content: "website" }),
|
|
488
|
-
/* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:title", content: fullTitle }),
|
|
489
|
-
description && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:description", content: description }),
|
|
490
|
-
fullImageUrl && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:image", content: fullImageUrl }),
|
|
491
|
-
resolvedUrl && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:url", content: resolvedUrl }),
|
|
492
|
-
/* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:locale", content: locale }),
|
|
493
|
-
productName && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:site_name", content: productName }),
|
|
494
|
-
/* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:card", content: "summary_large_image" }),
|
|
495
|
-
/* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:title", content: fullTitle }),
|
|
496
|
-
description && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:description", content: description }),
|
|
497
|
-
fullImageUrl && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:image", content: fullImageUrl }),
|
|
498
|
-
twitterHandle && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:creator", content: twitterHandle }),
|
|
499
|
-
/* @__PURE__ */ jsxRuntime.jsx("meta", { name: "theme-color", content: themeColor }),
|
|
500
|
-
structuredDataItems.map((data, index) => /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", children: serializeJsonLd(data) }, `ld-${index}`)),
|
|
501
|
-
additionalMeta?.map((meta, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
502
|
-
"meta",
|
|
503
|
-
{
|
|
504
|
-
...meta.name ? { name: meta.name } : {},
|
|
505
|
-
...meta.property ? { property: meta.property } : {},
|
|
506
|
-
content: meta.content
|
|
507
|
-
},
|
|
508
|
-
`meta-${index}`
|
|
509
|
-
))
|
|
510
|
-
] });
|
|
511
|
-
}
|
|
512
|
-
function cn(...inputs) {
|
|
513
|
-
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
514
|
-
}
|
|
515
|
-
var buttonVariants = classVarianceAuthority.cva(
|
|
516
|
-
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
517
|
-
{
|
|
518
|
-
variants: {
|
|
519
|
-
variant: {
|
|
520
|
-
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
|
521
|
-
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
|
522
|
-
outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
|
523
|
-
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
|
524
|
-
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
525
|
-
link: "text-primary underline-offset-4 hover:underline"
|
|
526
|
-
},
|
|
527
|
-
size: {
|
|
528
|
-
default: "h-9 px-4 py-2",
|
|
529
|
-
sm: "h-8 rounded-md px-3 text-xs",
|
|
530
|
-
lg: "h-10 rounded-md px-8",
|
|
531
|
-
icon: "h-9 w-9"
|
|
532
|
-
}
|
|
533
|
-
},
|
|
534
|
-
defaultVariants: {
|
|
535
|
-
variant: "default",
|
|
536
|
-
size: "default"
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
);
|
|
540
|
-
var Button = React__namespace.forwardRef(
|
|
541
|
-
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
542
|
-
const Comp = asChild ? reactSlot.Slot : "button";
|
|
543
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
544
|
-
Comp,
|
|
545
|
-
{
|
|
546
|
-
className: cn(buttonVariants({ variant, size, className })),
|
|
547
|
-
ref,
|
|
548
|
-
...props
|
|
549
|
-
}
|
|
550
|
-
);
|
|
551
|
-
}
|
|
552
|
-
);
|
|
553
|
-
Button.displayName = "Button";
|
|
554
|
-
var GET_PLANS_QUERY = `
|
|
555
|
-
query GetPublicPlans($productId: String!) {
|
|
556
|
-
publicPlans(productId: $productId) {
|
|
557
|
-
id
|
|
558
|
-
name
|
|
559
|
-
price
|
|
560
|
-
currency
|
|
561
|
-
currencyPrices
|
|
562
|
-
duration
|
|
563
|
-
features {
|
|
564
|
-
id
|
|
565
|
-
quotaId
|
|
566
|
-
context
|
|
567
|
-
quota {
|
|
568
|
-
id
|
|
569
|
-
name
|
|
570
|
-
limits
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
isActive
|
|
574
|
-
productID
|
|
575
|
-
createdAt
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
`;
|
|
579
|
-
var PUBLIC_PRODUCT_KEYS_QUERY = `
|
|
580
|
-
query PricingProductKeys {
|
|
581
|
-
publicProducts(limit: 200) {
|
|
582
|
-
items {
|
|
583
|
-
id
|
|
584
|
-
slug
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
`;
|
|
589
|
-
var CURRENCY_STORAGE_KEY = "burdenoff.pricing.currency";
|
|
590
|
-
function readStoredCurrency() {
|
|
591
|
-
try {
|
|
592
|
-
if (typeof window === "undefined") return null;
|
|
593
|
-
return window.localStorage.getItem(CURRENCY_STORAGE_KEY);
|
|
594
|
-
} catch {
|
|
595
|
-
return null;
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
function storeCurrency(code) {
|
|
599
|
-
try {
|
|
600
|
-
if (typeof window === "undefined") return;
|
|
601
|
-
window.localStorage.setItem(CURRENCY_STORAGE_KEY, code);
|
|
602
|
-
} catch {
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
function readVisitorLocale() {
|
|
606
|
-
let timeZone = null;
|
|
607
|
-
try {
|
|
608
|
-
timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? null;
|
|
609
|
-
} catch {
|
|
610
|
-
timeZone = null;
|
|
611
|
-
}
|
|
612
|
-
const languages = typeof navigator === "undefined" ? [] : [...navigator.languages ?? [], navigator.language].filter(
|
|
613
|
-
(l) => typeof l === "string" && l.length > 0
|
|
614
|
-
);
|
|
615
|
-
return { timeZone, languages };
|
|
616
|
-
}
|
|
617
|
-
function formatPrice(price, currencyCode = "USD", currencies) {
|
|
618
|
-
const currency = currencies?.[currencyCode];
|
|
619
|
-
if (!currency) {
|
|
620
|
-
return formatCatalogPrice(price, currencyCode);
|
|
621
|
-
}
|
|
622
|
-
const formattedNumber = price % 1 === 0 ? price.toString() : price.toFixed(2);
|
|
623
|
-
return currency.position === "prefix" ? `${currency.symbol}${formattedNumber}` : `${formattedNumber}${currency.symbol}`;
|
|
624
|
-
}
|
|
625
|
-
function formatInterval(duration, billingIntervals) {
|
|
626
|
-
const defaultIntervals = {
|
|
627
|
-
monthly: "/month",
|
|
628
|
-
yearly: "/year"
|
|
629
|
-
};
|
|
630
|
-
const intervals = billingIntervals || defaultIntervals;
|
|
631
|
-
return intervals[duration] || `/${duration}`;
|
|
632
|
-
}
|
|
633
|
-
function isEnterprisePlan(planName) {
|
|
634
|
-
const name = planName.toLowerCase();
|
|
635
|
-
return name.includes("team") || name.includes("business") || name.includes("enterprise");
|
|
636
|
-
}
|
|
637
|
-
function isEnterpriseAudiencePlan(plan) {
|
|
638
|
-
if (!isEnterprisePlan(plan.name)) {
|
|
639
|
-
return false;
|
|
640
|
-
}
|
|
641
|
-
if (typeof plan.priceRaw === "number") {
|
|
642
|
-
return plan.priceRaw === 0;
|
|
643
|
-
}
|
|
644
|
-
return true;
|
|
645
|
-
}
|
|
646
|
-
function splitPlansByAudience(plans, showAudienceTabs) {
|
|
647
|
-
if (!showAudienceTabs) {
|
|
648
|
-
return { individualPlans: plans, enterprisePlans: [] };
|
|
649
|
-
}
|
|
650
|
-
return {
|
|
651
|
-
individualPlans: plans.filter((p) => !isEnterpriseAudiencePlan(p)),
|
|
652
|
-
enterprisePlans: plans.filter((p) => isEnterpriseAudiencePlan(p))
|
|
653
|
-
};
|
|
654
|
-
}
|
|
655
|
-
function resolveEnterprisePlans(apiEnterprisePlans, staticEnterprisePlans, defaultEnterpriseCard) {
|
|
656
|
-
const staticPlans = staticEnterprisePlans || [];
|
|
657
|
-
if (apiEnterprisePlans.length === 0 && staticPlans.length === 0) {
|
|
658
|
-
return [defaultEnterpriseCard];
|
|
659
|
-
}
|
|
660
|
-
const apiNames = new Set(apiEnterprisePlans.map((p) => p.name.toLowerCase()));
|
|
661
|
-
const dedupedStaticPlans = staticPlans.filter(
|
|
662
|
-
(p) => !apiNames.has(p.name.toLowerCase())
|
|
663
|
-
);
|
|
664
|
-
return [...apiEnterprisePlans, ...dedupedStaticPlans];
|
|
665
|
-
}
|
|
666
|
-
function detectPlanTier(planName) {
|
|
667
|
-
const name = planName.toLowerCase();
|
|
668
|
-
if (name.includes("free") || name.includes("basic") || name.includes("starter") || name.includes("hobby")) {
|
|
669
|
-
return "basic";
|
|
670
|
-
} else if (name.includes("pro") || name.includes("professional") || name.includes("premium") || name.includes("team")) {
|
|
671
|
-
return "professional";
|
|
672
|
-
} else if (name.includes("enterprise") || name.includes("business")) {
|
|
673
|
-
return "enterprise";
|
|
674
|
-
}
|
|
675
|
-
return null;
|
|
676
|
-
}
|
|
677
|
-
function getFallbackFeaturesForTier(tier, fallbackFeatures) {
|
|
678
|
-
return [...fallbackFeatures?.[tier] || []];
|
|
679
|
-
}
|
|
680
|
-
function getMetadataForPlan(planName, priceRaw, planMetadata) {
|
|
681
|
-
const normalizedName = planName.toLowerCase();
|
|
682
|
-
const cleanName = cleanPlanDisplayName(planName);
|
|
683
|
-
const meta = planMetadata?.[normalizedName] ?? planMetadata?.[cleanName.toLowerCase()];
|
|
684
|
-
if (meta) {
|
|
685
|
-
return {
|
|
686
|
-
tier: meta.tier || "basic",
|
|
687
|
-
displayName: meta.displayName || cleanName,
|
|
688
|
-
defaultDescription: meta.defaultDescription || "Choose this plan",
|
|
689
|
-
popular: meta.popular ?? false,
|
|
690
|
-
ctaText: meta.ctaText || "Get Started",
|
|
691
|
-
ctaType: meta.ctaType || "signup",
|
|
692
|
-
features: meta.features
|
|
693
|
-
};
|
|
694
|
-
}
|
|
695
|
-
const tier = detectPlanTier(planName);
|
|
696
|
-
if (tier) {
|
|
697
|
-
const isPaidEnterpriseName = tier === "enterprise" && typeof priceRaw === "number" && priceRaw > 0;
|
|
698
|
-
if (isPaidEnterpriseName) {
|
|
699
|
-
return {
|
|
700
|
-
tier: "professional",
|
|
701
|
-
displayName: cleanName,
|
|
702
|
-
defaultDescription: "Choose this plan",
|
|
703
|
-
popular: false,
|
|
704
|
-
ctaText: "Get Started",
|
|
705
|
-
ctaType: "signup"
|
|
706
|
-
};
|
|
707
|
-
}
|
|
708
|
-
return {
|
|
709
|
-
tier,
|
|
710
|
-
displayName: cleanName,
|
|
711
|
-
defaultDescription: "Choose this plan",
|
|
712
|
-
popular: tier === "professional",
|
|
713
|
-
ctaText: tier === "enterprise" ? "Contact Sales" : "Get Started",
|
|
714
|
-
ctaType: tier === "enterprise" ? "contact" : "signup"
|
|
715
|
-
};
|
|
716
|
-
}
|
|
717
|
-
return {
|
|
718
|
-
tier: "basic",
|
|
719
|
-
displayName: cleanName,
|
|
720
|
-
defaultDescription: "Choose this plan",
|
|
721
|
-
popular: false,
|
|
722
|
-
ctaText: "Get Started",
|
|
723
|
-
ctaType: "signup"
|
|
724
|
-
};
|
|
725
|
-
}
|
|
726
|
-
function humanizeQuotaName(name) {
|
|
727
|
-
const parts = name.split(".");
|
|
728
|
-
const resource = parts.length >= 3 ? parts[parts.length - 2] : parts[parts.length - 1];
|
|
729
|
-
return resource.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
730
|
-
}
|
|
731
|
-
function transformFeatureToLabel(feature) {
|
|
732
|
-
const quota = feature.quota;
|
|
733
|
-
if (!quota) return "";
|
|
734
|
-
const { name, limits } = quota;
|
|
735
|
-
const featureContext = feature.context && typeof feature.context === "object" ? feature.context : null;
|
|
736
|
-
const limitsObj = limits && typeof limits === "object" ? limits : null;
|
|
737
|
-
const value = typeof featureContext?.quantity === "number" ? featureContext.quantity : typeof limitsObj?.value === "number" ? limitsObj.value : void 0;
|
|
738
|
-
const unit = typeof featureContext?.unit === "string" && featureContext.unit || typeof limitsObj?.unit === "string" && limitsObj.unit || void 0;
|
|
739
|
-
const description = typeof featureContext?.description === "string" && featureContext.description || typeof limitsObj?.description === "string" && limitsObj.description || void 0;
|
|
740
|
-
const type = limitsObj?.type;
|
|
741
|
-
if (description) {
|
|
742
|
-
if (type === "number" && typeof value === "number") {
|
|
743
|
-
return `${value.toLocaleString()} ${description}`;
|
|
744
|
-
}
|
|
745
|
-
return description;
|
|
746
|
-
}
|
|
747
|
-
if (unit) {
|
|
748
|
-
if (typeof value === "number") {
|
|
749
|
-
return `${value.toLocaleString()} ${unit}`;
|
|
750
|
-
}
|
|
751
|
-
return unit;
|
|
752
|
-
}
|
|
753
|
-
const label = humanizeQuotaName(name);
|
|
754
|
-
if (type === "boolean") {
|
|
755
|
-
return label;
|
|
756
|
-
}
|
|
757
|
-
if (typeof value === "number") {
|
|
758
|
-
return `${value.toLocaleString()} ${label}`;
|
|
759
|
-
}
|
|
760
|
-
return label;
|
|
761
|
-
}
|
|
762
|
-
function extractDisplayFeatures(features, planName, fallbackFeatures) {
|
|
763
|
-
const tier = detectPlanTier(planName);
|
|
764
|
-
if (tier) {
|
|
765
|
-
const tierFeatures = getFallbackFeaturesForTier(tier, fallbackFeatures);
|
|
766
|
-
if (tierFeatures.length > 0) {
|
|
767
|
-
return tierFeatures;
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
const quotaFeatures = features.filter((f) => f.quota).map((f) => transformFeatureToLabel(f));
|
|
771
|
-
if (quotaFeatures.length > 0) {
|
|
772
|
-
return quotaFeatures;
|
|
773
|
-
}
|
|
774
|
-
return [];
|
|
775
|
-
}
|
|
776
|
-
function transformToDisplayCard(plan, props, currency) {
|
|
777
|
-
const metadata = getMetadataForPlan(
|
|
778
|
-
plan.name,
|
|
779
|
-
plan.price,
|
|
780
|
-
props.planMetadata
|
|
781
|
-
);
|
|
782
|
-
const selectedPrice = currency ? planPriceIn(plan, currency) : null;
|
|
783
|
-
const formattedPrice = currency && selectedPrice !== null ? formatPrice(selectedPrice, currency.toUpperCase(), props.currencies) : formatPrice(plan.price, plan.currency, props.currencies);
|
|
784
|
-
const interval = formatInterval(plan.duration, props.billingIntervals);
|
|
785
|
-
const features = metadata.features && metadata.features.length > 0 ? metadata.features : extractDisplayFeatures(
|
|
786
|
-
plan.features,
|
|
787
|
-
plan.name,
|
|
788
|
-
props.fallbackFeatures
|
|
789
|
-
);
|
|
790
|
-
const contactUrl = props.contactUrl || "/contact";
|
|
791
|
-
const ctaLink = metadata.ctaType === "contact" ? contactUrl : props.appLoginUrl;
|
|
792
|
-
const isQuoteBased = metadata.ctaType === "contact" && plan.price === 0;
|
|
793
|
-
return {
|
|
794
|
-
id: plan.id,
|
|
795
|
-
name: metadata.displayName,
|
|
796
|
-
description: metadata.defaultDescription,
|
|
797
|
-
price: isQuoteBased ? "Custom" : formattedPrice,
|
|
798
|
-
priceRaw: plan.price,
|
|
799
|
-
interval: isQuoteBased ? "" : interval,
|
|
800
|
-
duration: plan.duration,
|
|
801
|
-
features,
|
|
802
|
-
popular: metadata.popular,
|
|
803
|
-
ctaText: metadata.ctaText,
|
|
804
|
-
ctaLink,
|
|
805
|
-
tier: metadata.tier
|
|
806
|
-
};
|
|
807
|
-
}
|
|
808
|
-
function PricingSection(props) {
|
|
809
|
-
const {
|
|
810
|
-
sectionHeading = "Pricing Plans",
|
|
811
|
-
sectionSubtitle = "Choose the perfect plan for your needs",
|
|
812
|
-
id,
|
|
813
|
-
className = ""
|
|
814
|
-
} = props;
|
|
815
|
-
const client = useWebSDK();
|
|
816
|
-
const config = useWebSDKConfig();
|
|
817
|
-
const { product: contextProduct } = useProduct();
|
|
818
|
-
const requestedProductId = (props.productId || config.productId || "").trim();
|
|
819
|
-
const contextKey = resolvePricingProductKey(requestedProductId, [
|
|
820
|
-
contextProduct
|
|
821
|
-
]);
|
|
822
|
-
const productKey = contextKey ?? requestedProductId;
|
|
823
|
-
const [catalogPlans, setCatalogPlans] = React.useState([]);
|
|
824
|
-
const [loading, setLoading] = React.useState(true);
|
|
825
|
-
const [error, setError] = React.useState(null);
|
|
826
|
-
const [billingCycle, setBillingCycle] = React.useState(
|
|
827
|
-
"monthly"
|
|
828
|
-
);
|
|
829
|
-
const [audience, setAudience] = React.useState(
|
|
830
|
-
"individual"
|
|
831
|
-
);
|
|
832
|
-
const [selectedCurrency, setSelectedCurrency] = React.useState(
|
|
833
|
-
props.defaultCurrency ? props.defaultCurrency.toUpperCase() : null
|
|
834
|
-
);
|
|
835
|
-
React.useEffect(() => {
|
|
836
|
-
let cancelled = false;
|
|
837
|
-
async function resolveKey() {
|
|
838
|
-
if (!productKey) {
|
|
839
|
-
throw new Error(
|
|
840
|
-
"PricingPage needs a product slug: pass productId or set WebSDKConfig.productId."
|
|
841
|
-
);
|
|
842
|
-
}
|
|
843
|
-
if (!isUuidLike(productKey)) return productKey;
|
|
844
|
-
const response = await client.query(
|
|
845
|
-
PUBLIC_PRODUCT_KEYS_QUERY
|
|
846
|
-
);
|
|
847
|
-
if (response.errors && response.errors.length > 0) {
|
|
848
|
-
throw new Error(response.errors[0].message);
|
|
849
|
-
}
|
|
850
|
-
const resolved = resolvePricingProductKey(
|
|
851
|
-
productKey,
|
|
852
|
-
response.data?.publicProducts?.items ?? []
|
|
853
|
-
);
|
|
854
|
-
if (!resolved) throw new PricingProductKeyError(productKey);
|
|
855
|
-
return resolved;
|
|
856
|
-
}
|
|
857
|
-
async function loadPlans() {
|
|
858
|
-
try {
|
|
859
|
-
setLoading(true);
|
|
860
|
-
const key = await resolveKey();
|
|
861
|
-
if (cancelled) return;
|
|
862
|
-
const response = await client.query(GET_PLANS_QUERY, {
|
|
863
|
-
productId: key
|
|
864
|
-
});
|
|
865
|
-
if (cancelled) return;
|
|
866
|
-
if (response.errors && response.errors.length > 0) {
|
|
867
|
-
throw new Error(response.errors[0].message);
|
|
868
|
-
}
|
|
869
|
-
if (!response.data?.publicPlans) {
|
|
870
|
-
throw new Error("No plans data received");
|
|
871
|
-
}
|
|
872
|
-
const active = response.data.publicPlans.filter(
|
|
873
|
-
(plan) => plan.isActive
|
|
874
|
-
);
|
|
875
|
-
if (active.length === 0) {
|
|
876
|
-
throw new Error(
|
|
877
|
-
`publicPlans returned no active plans for product "${key}".`
|
|
878
|
-
);
|
|
879
|
-
}
|
|
880
|
-
setCatalogPlans(active);
|
|
881
|
-
setError(null);
|
|
882
|
-
} catch (err) {
|
|
883
|
-
if (cancelled) return;
|
|
884
|
-
console.error("Failed to load plans:", err);
|
|
885
|
-
setCatalogPlans([]);
|
|
886
|
-
setError("Unable to load pricing information. Please try again later.");
|
|
887
|
-
} finally {
|
|
888
|
-
if (!cancelled) {
|
|
889
|
-
setLoading(false);
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
loadPlans();
|
|
894
|
-
return () => {
|
|
895
|
-
cancelled = true;
|
|
896
|
-
};
|
|
897
|
-
}, [client, productKey]);
|
|
898
|
-
const currencyOptions = React.useMemo(
|
|
899
|
-
() => availablePlanCurrencies(catalogPlans),
|
|
900
|
-
[catalogPlans]
|
|
901
|
-
);
|
|
902
|
-
React.useEffect(() => {
|
|
903
|
-
if (selectedCurrency || currencyOptions.length < 2) return;
|
|
904
|
-
const stored = readStoredCurrency()?.toUpperCase();
|
|
905
|
-
if (stored && currencyOptions.includes(stored)) {
|
|
906
|
-
setSelectedCurrency(stored);
|
|
907
|
-
return;
|
|
908
|
-
}
|
|
909
|
-
const regional = detectRegionalCurrency(
|
|
910
|
-
currencyOptions,
|
|
911
|
-
readVisitorLocale()
|
|
912
|
-
);
|
|
913
|
-
if (regional) setSelectedCurrency(regional);
|
|
914
|
-
}, [currencyOptions, selectedCurrency]);
|
|
915
|
-
const activeCurrency = selectedCurrency && currencyOptions.includes(selectedCurrency) ? selectedCurrency : currencyOptions[0] ?? null;
|
|
916
|
-
const allPlans = catalogPlans.map((plan) => transformToDisplayCard(plan, props, activeCurrency)).sort((a, b) => {
|
|
917
|
-
const aCustom = a.price === "Custom" ? 1 : 0;
|
|
918
|
-
const bCustom = b.price === "Custom" ? 1 : 0;
|
|
919
|
-
return aCustom - bCustom || a.priceRaw - b.priceRaw;
|
|
920
|
-
});
|
|
921
|
-
const showCurrencySelector = props.showCurrencySelector !== false && currencyOptions.length > 1;
|
|
922
|
-
const {
|
|
923
|
-
individualPlans: individualAllPlans,
|
|
924
|
-
enterprisePlans: enterpriseApiPlans
|
|
925
|
-
} = splitPlansByAudience(allPlans, props.showAudienceTabs);
|
|
926
|
-
const individualPlans = plansForBillingCycle(
|
|
927
|
-
individualAllPlans,
|
|
928
|
-
billingCycle
|
|
929
|
-
);
|
|
930
|
-
const enterpriseCards = enterpriseApiPlans.map((p) => {
|
|
931
|
-
if (p.priceRaw === 0 && p.name.toLowerCase().includes("enterprise")) {
|
|
932
|
-
return {
|
|
933
|
-
...p,
|
|
934
|
-
price: "Custom",
|
|
935
|
-
interval: "",
|
|
936
|
-
ctaText: "Contact Sales",
|
|
937
|
-
ctaLink: props.contactUrl || "/contact"
|
|
938
|
-
};
|
|
939
|
-
}
|
|
940
|
-
return p;
|
|
941
|
-
});
|
|
942
|
-
const defaultEnterpriseCard = {
|
|
943
|
-
name: "Enterprise",
|
|
944
|
-
subtitle: "For organizations that need more",
|
|
945
|
-
description: "For organizations that need more",
|
|
946
|
-
price: "Custom",
|
|
947
|
-
period: "",
|
|
948
|
-
interval: "",
|
|
949
|
-
button: "Contact Sales",
|
|
950
|
-
buttonLink: props.contactUrl || "/contact",
|
|
951
|
-
ctaText: "Contact Sales",
|
|
952
|
-
ctaLink: props.contactUrl || "/contact",
|
|
953
|
-
popular: false,
|
|
954
|
-
features: props.fallbackFeatures?.enterprise || [
|
|
955
|
-
"Unlimited everything",
|
|
956
|
-
"SSO & SAML",
|
|
957
|
-
"Audit logs",
|
|
958
|
-
"Dedicated support",
|
|
959
|
-
"Self-hosted option"
|
|
960
|
-
]
|
|
961
|
-
};
|
|
962
|
-
const resolvedEnterprisePlans = resolveEnterprisePlans(
|
|
963
|
-
enterpriseCards,
|
|
964
|
-
props.enterprisePlans,
|
|
965
|
-
defaultEnterpriseCard
|
|
966
|
-
);
|
|
967
|
-
const plans = props.showAudienceTabs && audience === "enterprise" ? [] : individualPlans;
|
|
968
|
-
if (loading) {
|
|
969
|
-
return /* @__PURE__ */ jsxRuntime.jsx("section", { id, className: `py-12 sm:py-16 md:py-20 ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "container mx-auto px-4 sm:px-6", children: [
|
|
970
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-8 sm:mb-12", children: [
|
|
971
|
-
sectionHeading ? /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl sm:text-3xl md:text-4xl font-bold mb-3 sm:mb-4", children: sectionHeading }) : (
|
|
972
|
-
// Visually hidden but DOM-present heading so axe's heading-order
|
|
973
|
-
// rule still sees an h2 between the consumer's hero h1 and the
|
|
974
|
-
// h3 plan-card titles. See `getMetadataForPlan` for why a falsy
|
|
975
|
-
// sectionHeading is the canonical "no visible heading" signal.
|
|
976
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sr-only", children: "Pricing" })
|
|
977
|
-
),
|
|
978
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm sm:text-base md:text-lg", children: "Loading pricing information..." })
|
|
979
|
-
] }),
|
|
980
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-3 gap-6 sm:gap-8", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
981
|
-
"div",
|
|
982
|
-
{
|
|
983
|
-
className: "bg-card border rounded-lg p-6 animate-pulse",
|
|
984
|
-
children: [
|
|
985
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 bg-muted rounded w-1/2 mb-4" }),
|
|
986
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-3/4 mb-6" }),
|
|
987
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-10 bg-muted rounded w-1/3 mb-6" }),
|
|
988
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3 mb-6", children: [1, 2, 3, 4].map((j) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded" }, j)) }),
|
|
989
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-10 bg-muted rounded" })
|
|
990
|
-
]
|
|
991
|
-
},
|
|
992
|
-
i
|
|
993
|
-
)) })
|
|
994
|
-
] }) });
|
|
995
|
-
}
|
|
996
|
-
if (error && plans.length === 0) {
|
|
997
|
-
return /* @__PURE__ */ jsxRuntime.jsx("section", { id, className: `py-12 sm:py-16 md:py-20 ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "container mx-auto px-4 sm:px-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
998
|
-
sectionHeading ? /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl sm:text-3xl md:text-4xl font-bold mb-4", children: sectionHeading }) : /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sr-only", children: "Pricing" }),
|
|
999
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-8", children: error }),
|
|
1000
|
-
/* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: () => window.location.reload(), children: "Retry" })
|
|
1001
|
-
] }) }) });
|
|
1002
|
-
}
|
|
1003
|
-
return /* @__PURE__ */ jsxRuntime.jsx("section", { id, className: `py-12 sm:py-16 md:py-20 ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "container mx-auto px-4 sm:px-6", children: [
|
|
1004
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-8 sm:mb-12", children: [
|
|
1005
|
-
sectionHeading ? /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl sm:text-3xl md:text-4xl font-bold mb-3 sm:mb-4", children: sectionHeading }) : /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sr-only", children: "Pricing" }),
|
|
1006
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm sm:text-base md:text-lg mb-6", children: sectionSubtitle }),
|
|
1007
|
-
props.showAudienceTabs && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center mb-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center bg-muted p-1.5 rounded-xl", children: [
|
|
1008
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1009
|
-
"button",
|
|
1010
|
-
{
|
|
1011
|
-
type: "button",
|
|
1012
|
-
onClick: () => setAudience("individual"),
|
|
1013
|
-
className: `px-6 sm:px-8 py-2.5 rounded-lg text-sm font-semibold transition-all ${audience === "individual" ? "bg-background shadow-sm text-foreground" : "text-muted-foreground hover:text-foreground"}`,
|
|
1014
|
-
children: "Individual"
|
|
1015
|
-
}
|
|
1016
|
-
),
|
|
1017
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1018
|
-
"button",
|
|
1019
|
-
{
|
|
1020
|
-
type: "button",
|
|
1021
|
-
onClick: () => setAudience("enterprise"),
|
|
1022
|
-
className: `px-6 sm:px-8 py-2.5 rounded-lg text-sm font-semibold transition-all ${audience === "enterprise" ? "bg-background shadow-sm text-foreground" : "text-muted-foreground hover:text-foreground"}`,
|
|
1023
|
-
children: "Enterprise"
|
|
1024
|
-
}
|
|
1025
|
-
)
|
|
1026
|
-
] }) }),
|
|
1027
|
-
(audience === "individual" || !props.showAudienceTabs) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center bg-muted/60 p-1.5 rounded-xl", children: [
|
|
1028
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1029
|
-
"button",
|
|
1030
|
-
{
|
|
1031
|
-
type: "button",
|
|
1032
|
-
onClick: () => setBillingCycle("monthly"),
|
|
1033
|
-
className: `px-5 sm:px-6 py-2 rounded-lg text-sm font-medium transition-all ${billingCycle === "monthly" ? "bg-background shadow-sm text-foreground" : "text-muted-foreground hover:text-foreground"}`,
|
|
1034
|
-
children: "Monthly"
|
|
1035
|
-
}
|
|
1036
|
-
),
|
|
1037
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1038
|
-
"button",
|
|
1039
|
-
{
|
|
1040
|
-
type: "button",
|
|
1041
|
-
onClick: () => setBillingCycle("yearly"),
|
|
1042
|
-
className: `px-5 sm:px-6 py-2 rounded-lg text-sm font-medium transition-all ${billingCycle === "yearly" ? "bg-background shadow-sm text-foreground" : "text-muted-foreground hover:text-foreground"}`,
|
|
1043
|
-
children: "Yearly"
|
|
1044
|
-
}
|
|
1045
|
-
)
|
|
1046
|
-
] }) }),
|
|
1047
|
-
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: [
|
|
1048
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Currency" }),
|
|
1049
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1050
|
-
"select",
|
|
1051
|
-
{
|
|
1052
|
-
value: activeCurrency ?? "",
|
|
1053
|
-
onChange: (event) => {
|
|
1054
|
-
const code = event.target.value;
|
|
1055
|
-
setSelectedCurrency(code);
|
|
1056
|
-
storeCurrency(code);
|
|
1057
|
-
},
|
|
1058
|
-
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",
|
|
1059
|
-
"data-testid": "pricing-currency-select",
|
|
1060
|
-
children: currencyOptions.map((code) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: code, children: code }, code))
|
|
1061
|
-
}
|
|
1062
|
-
)
|
|
1063
|
-
] }) })
|
|
1064
|
-
] }),
|
|
1065
|
-
props.showAudienceTabs && audience === "enterprise" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto max-w-3xl text-center mb-8", children: [
|
|
1066
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl sm:text-2xl font-bold text-foreground", children: props.enterpriseHeading || "Enterprise Solutions" }),
|
|
1067
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-3 text-muted-foreground", children: props.enterpriseSubtitle || "Built for teams, agencies, and organizations that need advanced collaboration, security, support, and scale." })
|
|
1068
|
-
] }),
|
|
1069
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-3 gap-6 sm:gap-8 max-w-6xl mx-auto items-stretch", children: props.showAudienceTabs && audience === "enterprise" ? (
|
|
1070
|
-
/* Enterprise cards — from API, static prop, or built-in default */
|
|
1071
|
-
resolvedEnterprisePlans.map((ep) => {
|
|
1072
|
-
const subtitle = ep.subtitle || ep.description || "";
|
|
1073
|
-
const btnText = ep.button || ep.ctaText || "Get Started";
|
|
1074
|
-
const btnLink = ep.buttonLink || ep.ctaLink || props.appLoginUrl;
|
|
1075
|
-
const period = ep.period || ep.interval || "";
|
|
1076
|
-
const note = ep.note || "";
|
|
1077
|
-
const badge = ep.badge || "";
|
|
1078
|
-
const isPopular = ep.popular;
|
|
1079
|
-
const features = ep.features || [];
|
|
1080
|
-
const price = String(ep.price);
|
|
1081
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1082
|
-
"div",
|
|
1083
|
-
{
|
|
1084
|
-
className: `bg-card rounded-xl p-6 sm:p-8 transition-all duration-300 flex flex-col ${isPopular ? "border-2 border-primary shadow-xl ring-1 ring-primary/20" : "border hover:shadow-lg hover:border-primary/50"}`,
|
|
1085
|
-
children: [
|
|
1086
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-6", children: [
|
|
1087
|
-
isPopular && badge && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block bg-primary text-primary-foreground px-4 py-1 rounded-full text-xs font-semibold mb-3", children: badge }),
|
|
1088
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl sm:text-2xl font-bold mb-2", children: ep.name }),
|
|
1089
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm", children: subtitle })
|
|
1090
|
-
] }),
|
|
1091
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-2", children: [
|
|
1092
|
-
price === "Custom" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-3xl sm:text-4xl font-bold", children: "Custom" }) : /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "flex items-baseline justify-center gap-1", children: [
|
|
1093
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-4xl sm:text-5xl font-extrabold", children: price }),
|
|
1094
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-lg", children: period })
|
|
1095
|
-
] }),
|
|
1096
|
-
note && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm font-medium text-primary", children: note })
|
|
1097
|
-
] }),
|
|
1098
|
-
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-3 mb-8 mt-6 flex-1", children: features.map((feature) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1099
|
-
"li",
|
|
1100
|
-
{
|
|
1101
|
-
className: "text-sm flex items-start gap-2",
|
|
1102
|
-
children: [
|
|
1103
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary mt-0.5 flex-shrink-0", children: "\u2713" }),
|
|
1104
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: feature })
|
|
1105
|
-
]
|
|
1106
|
-
},
|
|
1107
|
-
feature
|
|
1108
|
-
)) }),
|
|
1109
|
-
btnLink.startsWith("/") ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1110
|
-
Button,
|
|
1111
|
-
{
|
|
1112
|
-
className: "w-full h-11",
|
|
1113
|
-
variant: isPopular ? "default" : "outline",
|
|
1114
|
-
size: "lg",
|
|
1115
|
-
asChild: true,
|
|
1116
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1117
|
-
"a",
|
|
1118
|
-
{
|
|
1119
|
-
href: btnLink,
|
|
1120
|
-
onClick: () => window.scrollTo({ top: 0, behavior: "smooth" }),
|
|
1121
|
-
children: btnText
|
|
1122
|
-
}
|
|
1123
|
-
)
|
|
1124
|
-
}
|
|
1125
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1126
|
-
Button,
|
|
1127
|
-
{
|
|
1128
|
-
className: "w-full h-11",
|
|
1129
|
-
variant: isPopular ? "default" : "outline",
|
|
1130
|
-
size: "lg",
|
|
1131
|
-
asChild: true,
|
|
1132
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1133
|
-
"a",
|
|
1134
|
-
{
|
|
1135
|
-
href: btnLink,
|
|
1136
|
-
target: "_blank",
|
|
1137
|
-
rel: "noopener noreferrer",
|
|
1138
|
-
children: btnText
|
|
1139
|
-
}
|
|
1140
|
-
)
|
|
1141
|
-
}
|
|
1142
|
-
)
|
|
1143
|
-
]
|
|
1144
|
-
},
|
|
1145
|
-
ep.name
|
|
1146
|
-
);
|
|
1147
|
-
})
|
|
1148
|
-
) : (
|
|
1149
|
-
/* Individual API-fetched cards */
|
|
1150
|
-
plans.map((plan) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1151
|
-
"div",
|
|
1152
|
-
{
|
|
1153
|
-
className: `bg-card rounded-xl p-6 sm:p-8 transition-all duration-300 flex flex-col ${plan.popular ? "border-2 border-primary shadow-xl ring-1 ring-primary/20" : "border hover:shadow-lg hover:border-primary/50"}`,
|
|
1154
|
-
children: [
|
|
1155
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-6", children: [
|
|
1156
|
-
plan.popular && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block bg-primary text-primary-foreground px-4 py-1 rounded-full text-xs font-semibold mb-3", children: "Most Popular" }),
|
|
1157
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl sm:text-2xl font-bold mb-2", children: plan.name }),
|
|
1158
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm", children: plan.description })
|
|
1159
|
-
] }),
|
|
1160
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center mb-8", children: plan.price === "Custom" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-3xl sm:text-4xl font-bold", children: "Custom" }) : /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "flex items-baseline justify-center gap-1", children: [
|
|
1161
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-4xl sm:text-5xl font-bold", children: plan.price }),
|
|
1162
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-lg", children: plan.interval })
|
|
1163
|
-
] }) }),
|
|
1164
|
-
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-3 mb-8 flex-1", children: plan.features.map((feature, index) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1165
|
-
"li",
|
|
1166
|
-
{
|
|
1167
|
-
className: "text-sm flex items-start gap-2",
|
|
1168
|
-
children: [
|
|
1169
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary mt-0.5 flex-shrink-0", children: "\u2713" }),
|
|
1170
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: feature })
|
|
1171
|
-
]
|
|
1172
|
-
},
|
|
1173
|
-
index
|
|
1174
|
-
)) }),
|
|
1175
|
-
plan.ctaLink.startsWith("/") ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1176
|
-
Button,
|
|
1177
|
-
{
|
|
1178
|
-
className: "w-full h-11",
|
|
1179
|
-
variant: plan.popular ? "default" : "outline",
|
|
1180
|
-
size: "lg",
|
|
1181
|
-
asChild: true,
|
|
1182
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1183
|
-
"a",
|
|
1184
|
-
{
|
|
1185
|
-
href: plan.ctaLink,
|
|
1186
|
-
onClick: () => window.scrollTo({ top: 0, behavior: "smooth" }),
|
|
1187
|
-
children: plan.ctaText
|
|
1188
|
-
}
|
|
1189
|
-
)
|
|
1190
|
-
}
|
|
1191
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1192
|
-
Button,
|
|
1193
|
-
{
|
|
1194
|
-
className: "w-full h-11",
|
|
1195
|
-
variant: plan.popular ? "default" : "outline",
|
|
1196
|
-
size: "lg",
|
|
1197
|
-
asChild: true,
|
|
1198
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1199
|
-
"a",
|
|
1200
|
-
{
|
|
1201
|
-
href: plan.ctaLink,
|
|
1202
|
-
target: "_blank",
|
|
1203
|
-
rel: "noopener noreferrer",
|
|
1204
|
-
children: plan.ctaText
|
|
1205
|
-
}
|
|
1206
|
-
)
|
|
1207
|
-
}
|
|
1208
|
-
)
|
|
1209
|
-
]
|
|
1210
|
-
},
|
|
1211
|
-
plan.id
|
|
1212
|
-
))
|
|
1213
|
-
) })
|
|
1214
|
-
] }) });
|
|
1215
|
-
}
|
|
1216
|
-
function PricingPage(props) {
|
|
1217
|
-
const {
|
|
1218
|
-
productName,
|
|
1219
|
-
heroHeading = "Simple, Transparent Pricing",
|
|
1220
|
-
heroSubtitle = "Choose the plan that's right for you. No hidden fees, no surprises.",
|
|
1221
|
-
seoTitle,
|
|
1222
|
-
seoDescription,
|
|
1223
|
-
seoKeywords,
|
|
1224
|
-
seoImage,
|
|
1225
|
-
seoUrl
|
|
1226
|
-
} = props;
|
|
1227
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
|
|
1228
|
-
seoTitle && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1229
|
-
PageHead,
|
|
1230
|
-
{
|
|
1231
|
-
title: seoTitle,
|
|
1232
|
-
description: seoDescription || heroSubtitle,
|
|
1233
|
-
productName,
|
|
1234
|
-
keywords: seoKeywords,
|
|
1235
|
-
image: seoImage,
|
|
1236
|
-
url: seoUrl
|
|
1237
|
-
}
|
|
1238
|
-
),
|
|
1239
|
-
/* @__PURE__ */ jsxRuntime.jsx("section", { className: "bg-gradient-to-b from-background to-muted/20 py-12 sm:py-16 md:py-20 lg:py-24", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center max-w-3xl mx-auto", children: [
|
|
1240
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold mb-4 sm:mb-6", children: heroHeading }),
|
|
1241
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg sm:text-xl text-muted-foreground leading-relaxed", children: heroSubtitle })
|
|
1242
|
-
] }) }) }),
|
|
1243
|
-
/* @__PURE__ */ jsxRuntime.jsx(PricingSection, { ...props })
|
|
1244
|
-
] });
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
exports.PricingPage = PricingPage;
|
|
1248
|
-
exports.PricingProductKeyError = PricingProductKeyError;
|
|
1249
|
-
exports.PricingSection = PricingSection;
|
|
1250
|
-
exports.availablePlanCurrencies = availablePlanCurrencies;
|
|
1251
|
-
exports.cleanPlanDisplayName = cleanPlanDisplayName;
|
|
1252
|
-
exports.isUuidLike = isUuidLike;
|
|
1253
|
-
exports.planPriceIn = planPriceIn;
|
|
1254
|
-
exports.resolveEnterprisePlans = resolveEnterprisePlans;
|
|
1255
|
-
exports.resolvePricingProductKey = resolvePricingProductKey;
|
|
1256
|
-
exports.splitPlansByAudience = splitPlansByAudience;
|
|
1257
|
-
exports.transformFeatureToLabel = transformFeatureToLabel;
|
|
1258
58
|
//# sourceMappingURL=pricing.js.map
|
|
1259
59
|
//# sourceMappingURL=pricing.js.map
|