@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/README.md
CHANGED
|
@@ -548,15 +548,33 @@ Newsletter subscription component with rate limiting.
|
|
|
548
548
|
|
|
549
549
|
### PricingPage / PricingSection
|
|
550
550
|
|
|
551
|
-
|
|
551
|
+
Pricing cards driven by the live billing catalog (`publicPlans`), with a
|
|
552
|
+
billing cycle toggle and a currency picker.
|
|
552
553
|
|
|
553
554
|
**Props:**
|
|
554
|
-
- `productName
|
|
555
|
-
- `productId
|
|
556
|
-
|
|
555
|
+
- `productName`: Product name
|
|
556
|
+
- `productId`: the product **slug** (e.g. `"vibecontrols"`); falls back to `WebSDKConfig.productId`.
|
|
557
|
+
A product UUID is mapped to its slug (via `ProductProvider`, else `publicProducts`);
|
|
558
|
+
a UUID no public product owns is refused with an error state, never a blank grid.
|
|
557
559
|
- `appLoginUrl` (required): App login URL for signup
|
|
558
560
|
- `contactUrl`: Contact page URL (default: '/contact')
|
|
561
|
+
- `planMetadata`: per-plan copy keyed by the lowercased catalog name
|
|
562
|
+
(`"vibecontrols - pro (monthly)"`) or the clean name (`"pro"`)
|
|
559
563
|
- `fallbackFeatures`: Fallback features when API doesn't provide them
|
|
564
|
+
- `defaultCurrency`: ISO code to show first; otherwise the base currency, then the
|
|
565
|
+
visitor's regional currency (time zone, then browser language) when the catalog sells in it
|
|
566
|
+
- `showCurrencySelector`: show the currency picker (default `true`; only rendered when
|
|
567
|
+
every plan is priced in more than one currency)
|
|
568
|
+
|
|
569
|
+
Behaviour:
|
|
570
|
+
- Card names drop the product prefix and billing period
|
|
571
|
+
(`"VibeControls - Hobby (Monthly)"` → `"Hobby"`); `planMetadata.displayName` wins.
|
|
572
|
+
- An empty or all-inactive catalog renders the error state and logs the product key.
|
|
573
|
+
- A `$0` contact-sales plan (e.g. Enterprise) shows as **Custom** and sorts last.
|
|
574
|
+
- A monthly-only free plan stays visible on the Yearly toggle.
|
|
575
|
+
- The catalog helpers (`cleanPlanDisplayName`, `planPriceIn`, `availablePlanCurrencies`,
|
|
576
|
+
`resolvePricingProductKey`, `isUuidLike`, `PricingProductKeyError`) are exported so a
|
|
577
|
+
website can pin its own `planMetadata` keys and tests to the same rules.
|
|
560
578
|
|
|
561
579
|
## License
|
|
562
580
|
|
|
@@ -1,30 +1,68 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Pure helpers that turn the billing catalog (`publicPlans`) into what the
|
|
5
|
+
* pricing page shows: which product key to query, the display name of a
|
|
6
|
+
* plan, which currencies a visitor can pick, and the price in that currency.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
|
|
8
|
+
* Kept free of React so every rule here is unit-tested directly
|
|
9
|
+
* (see `pricing-catalog.test.ts`).
|
|
10
|
+
*/
|
|
11
|
+
/** True when `value` is shaped like a UUID (a product registry id, not a slug). */
|
|
12
|
+
declare function isUuidLike(value: string): boolean;
|
|
13
|
+
/** A product as returned by `publicProduct` / `publicProducts`. */
|
|
14
|
+
interface ProductKeyCandidate {
|
|
15
|
+
id: string;
|
|
16
|
+
slug: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Thrown when the pricing page is given a product UUID that no public product
|
|
20
|
+
* owns. `publicPlans(productId)` matches on the product SLUG (a plan's
|
|
21
|
+
* `productID` is e.g. `"bigconsole"`), so a registry UUID silently returns an
|
|
22
|
+
* empty list — the page must refuse it instead of rendering a blank grid.
|
|
23
|
+
*/
|
|
24
|
+
declare class PricingProductKeyError extends Error {
|
|
25
|
+
constructor(productId: string);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the key `publicPlans` expects.
|
|
10
29
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
30
|
+
* - A slug is returned unchanged (trimmed).
|
|
31
|
+
* - A UUID is mapped to its product's slug using `candidates` (the product
|
|
32
|
+
* from `ProductProvider` and/or the `publicProducts` list).
|
|
33
|
+
* - A UUID with no matching candidate returns `null`: the caller must look it
|
|
34
|
+
* up or fail with {@link PricingProductKeyError}.
|
|
35
|
+
*/
|
|
36
|
+
declare function resolvePricingProductKey(productId: string, candidates: ReadonlyArray<ProductKeyCandidate | null | undefined>): string | null;
|
|
37
|
+
/**
|
|
38
|
+
* Catalog plan names carry the product and the billing period, e.g.
|
|
39
|
+
* `"VibeControls - Hobby (Monthly)"`. The card already sits on the product's
|
|
40
|
+
* own site and the period is chosen by the Monthly/Yearly toggle, so the
|
|
41
|
+
* visitor sees `"Hobby"`.
|
|
23
42
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
|
|
43
|
+
* Only the FIRST `" - "` separates the product, so `"Acme - Pro - Teams"`
|
|
44
|
+
* becomes `"Pro - Teams"`. A name that would end up empty is returned as is.
|
|
45
|
+
*/
|
|
46
|
+
declare function cleanPlanDisplayName(name: string): string;
|
|
47
|
+
/** The price-bearing fields of a catalog plan. */
|
|
48
|
+
interface PricedPlan {
|
|
49
|
+
price: number;
|
|
50
|
+
currency: string;
|
|
51
|
+
currencyPrices?: Record<string, number> | null;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The price of `plan` in `currency`, or `null` when the catalog has no price
|
|
55
|
+
* for it. A free plan costs 0 in every currency.
|
|
56
|
+
*/
|
|
57
|
+
declare function planPriceIn(plan: PricedPlan, currency: string): number | null;
|
|
58
|
+
/**
|
|
59
|
+
* The currencies a visitor can switch to: those in which EVERY plan has a
|
|
60
|
+
* price (free plans count as priced everywhere). A currency missing from a
|
|
61
|
+
* single paid plan is left out, so switching never blanks a card. Sorted with
|
|
62
|
+
* the catalog's base currency first, then alphabetically.
|
|
27
63
|
*/
|
|
64
|
+
declare function availablePlanCurrencies(plans: ReadonlyArray<PricedPlan>): string[];
|
|
65
|
+
|
|
28
66
|
interface PlanCard {
|
|
29
67
|
id: string;
|
|
30
68
|
name: string;
|
|
@@ -91,6 +129,18 @@ interface PricingProps {
|
|
|
91
129
|
* Billing interval display text
|
|
92
130
|
*/
|
|
93
131
|
billingIntervals?: Record<string, string>;
|
|
132
|
+
/**
|
|
133
|
+
* Currency to show first (ISO 4217, e.g. "INR"). When omitted the page shows
|
|
134
|
+
* the catalog's base currency until the browser reveals the visitor's
|
|
135
|
+
* region, then switches to that region's currency if the catalog sells in it.
|
|
136
|
+
*/
|
|
137
|
+
defaultCurrency?: string;
|
|
138
|
+
/**
|
|
139
|
+
* Show the currency picker when the catalog prices every plan in more than
|
|
140
|
+
* one currency.
|
|
141
|
+
* @default true
|
|
142
|
+
*/
|
|
143
|
+
showCurrencySelector?: boolean;
|
|
94
144
|
/**
|
|
95
145
|
* Plan metadata (tier info, CTA text, etc.)
|
|
96
146
|
*/
|
|
@@ -281,4 +331,4 @@ declare function PricingSection(props: PricingProps): react.JSX.Element;
|
|
|
281
331
|
*/
|
|
282
332
|
declare function PricingPage(props: PricingProps): react.JSX.Element;
|
|
283
333
|
|
|
284
|
-
export { type PlanCard, PricingPage, type PricingProps, PricingSection, resolveEnterprisePlans, splitPlansByAudience, transformFeatureToLabel };
|
|
334
|
+
export { type PlanCard, PricingPage, PricingProductKeyError, type PricingProps, PricingSection, availablePlanCurrencies, cleanPlanDisplayName, isUuidLike, planPriceIn, resolveEnterprisePlans, resolvePricingProductKey, splitPlansByAudience, transformFeatureToLabel };
|
|
@@ -1,30 +1,68 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Pure helpers that turn the billing catalog (`publicPlans`) into what the
|
|
5
|
+
* pricing page shows: which product key to query, the display name of a
|
|
6
|
+
* plan, which currencies a visitor can pick, and the price in that currency.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
|
|
8
|
+
* Kept free of React so every rule here is unit-tested directly
|
|
9
|
+
* (see `pricing-catalog.test.ts`).
|
|
10
|
+
*/
|
|
11
|
+
/** True when `value` is shaped like a UUID (a product registry id, not a slug). */
|
|
12
|
+
declare function isUuidLike(value: string): boolean;
|
|
13
|
+
/** A product as returned by `publicProduct` / `publicProducts`. */
|
|
14
|
+
interface ProductKeyCandidate {
|
|
15
|
+
id: string;
|
|
16
|
+
slug: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Thrown when the pricing page is given a product UUID that no public product
|
|
20
|
+
* owns. `publicPlans(productId)` matches on the product SLUG (a plan's
|
|
21
|
+
* `productID` is e.g. `"bigconsole"`), so a registry UUID silently returns an
|
|
22
|
+
* empty list — the page must refuse it instead of rendering a blank grid.
|
|
23
|
+
*/
|
|
24
|
+
declare class PricingProductKeyError extends Error {
|
|
25
|
+
constructor(productId: string);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the key `publicPlans` expects.
|
|
10
29
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
30
|
+
* - A slug is returned unchanged (trimmed).
|
|
31
|
+
* - A UUID is mapped to its product's slug using `candidates` (the product
|
|
32
|
+
* from `ProductProvider` and/or the `publicProducts` list).
|
|
33
|
+
* - A UUID with no matching candidate returns `null`: the caller must look it
|
|
34
|
+
* up or fail with {@link PricingProductKeyError}.
|
|
35
|
+
*/
|
|
36
|
+
declare function resolvePricingProductKey(productId: string, candidates: ReadonlyArray<ProductKeyCandidate | null | undefined>): string | null;
|
|
37
|
+
/**
|
|
38
|
+
* Catalog plan names carry the product and the billing period, e.g.
|
|
39
|
+
* `"VibeControls - Hobby (Monthly)"`. The card already sits on the product's
|
|
40
|
+
* own site and the period is chosen by the Monthly/Yearly toggle, so the
|
|
41
|
+
* visitor sees `"Hobby"`.
|
|
23
42
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
|
|
43
|
+
* Only the FIRST `" - "` separates the product, so `"Acme - Pro - Teams"`
|
|
44
|
+
* becomes `"Pro - Teams"`. A name that would end up empty is returned as is.
|
|
45
|
+
*/
|
|
46
|
+
declare function cleanPlanDisplayName(name: string): string;
|
|
47
|
+
/** The price-bearing fields of a catalog plan. */
|
|
48
|
+
interface PricedPlan {
|
|
49
|
+
price: number;
|
|
50
|
+
currency: string;
|
|
51
|
+
currencyPrices?: Record<string, number> | null;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The price of `plan` in `currency`, or `null` when the catalog has no price
|
|
55
|
+
* for it. A free plan costs 0 in every currency.
|
|
56
|
+
*/
|
|
57
|
+
declare function planPriceIn(plan: PricedPlan, currency: string): number | null;
|
|
58
|
+
/**
|
|
59
|
+
* The currencies a visitor can switch to: those in which EVERY plan has a
|
|
60
|
+
* price (free plans count as priced everywhere). A currency missing from a
|
|
61
|
+
* single paid plan is left out, so switching never blanks a card. Sorted with
|
|
62
|
+
* the catalog's base currency first, then alphabetically.
|
|
27
63
|
*/
|
|
64
|
+
declare function availablePlanCurrencies(plans: ReadonlyArray<PricedPlan>): string[];
|
|
65
|
+
|
|
28
66
|
interface PlanCard {
|
|
29
67
|
id: string;
|
|
30
68
|
name: string;
|
|
@@ -91,6 +129,18 @@ interface PricingProps {
|
|
|
91
129
|
* Billing interval display text
|
|
92
130
|
*/
|
|
93
131
|
billingIntervals?: Record<string, string>;
|
|
132
|
+
/**
|
|
133
|
+
* Currency to show first (ISO 4217, e.g. "INR"). When omitted the page shows
|
|
134
|
+
* the catalog's base currency until the browser reveals the visitor's
|
|
135
|
+
* region, then switches to that region's currency if the catalog sells in it.
|
|
136
|
+
*/
|
|
137
|
+
defaultCurrency?: string;
|
|
138
|
+
/**
|
|
139
|
+
* Show the currency picker when the catalog prices every plan in more than
|
|
140
|
+
* one currency.
|
|
141
|
+
* @default true
|
|
142
|
+
*/
|
|
143
|
+
showCurrencySelector?: boolean;
|
|
94
144
|
/**
|
|
95
145
|
* Plan metadata (tier info, CTA text, etc.)
|
|
96
146
|
*/
|
|
@@ -281,4 +331,4 @@ declare function PricingSection(props: PricingProps): react.JSX.Element;
|
|
|
281
331
|
*/
|
|
282
332
|
declare function PricingPage(props: PricingProps): react.JSX.Element;
|
|
283
333
|
|
|
284
|
-
export { type PlanCard, PricingPage, type PricingProps, PricingSection, resolveEnterprisePlans, splitPlansByAudience, transformFeatureToLabel };
|
|
334
|
+
export { type PlanCard, PricingPage, PricingProductKeyError, type PricingProps, PricingSection, availablePlanCurrencies, cleanPlanDisplayName, isUuidLike, planPriceIn, resolveEnterprisePlans, resolvePricingProductKey, splitPlansByAudience, transformFeatureToLabel };
|