@burdenoff/microfe-billing 2026.1014.3 → 2026.1023.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/dist/billing/modules/plans/constants/enterprise.js +50 -0
- package/dist/billing/modules/plans/constants/enterprise.js.map +1 -0
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js +124 -130
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
- package/dist/billing/providers/BillingProvider.js +15 -14
- package/dist/billing/providers/BillingProvider.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//#region src/billing/modules/plans/constants/enterprise.ts
|
|
2
|
+
var e = {
|
|
3
|
+
highlights: [
|
|
4
|
+
"Custom usage volumes",
|
|
5
|
+
"SSO / SCIM provisioning",
|
|
6
|
+
"Custom SLA & support contract",
|
|
7
|
+
"Volume-based custom pricing",
|
|
8
|
+
"Audit log with custom retention",
|
|
9
|
+
"Priority onboarding & migration"
|
|
10
|
+
],
|
|
11
|
+
summary: "SSO/SCIM · Custom SLA · Custom pricing",
|
|
12
|
+
contactEmail: "contact@burdenoff.com"
|
|
13
|
+
}, t = {
|
|
14
|
+
fluidgrids: {
|
|
15
|
+
highlights: [
|
|
16
|
+
"Custom workflow and run volumes",
|
|
17
|
+
"Custom storage and retention",
|
|
18
|
+
"SSO & SAML",
|
|
19
|
+
"SCIM provisioning",
|
|
20
|
+
"99.95%+ uptime SLA",
|
|
21
|
+
"Dedicated support with SLA",
|
|
22
|
+
"OEM embedding of the builder SDK",
|
|
23
|
+
"Data residency options"
|
|
24
|
+
],
|
|
25
|
+
summary: "Custom volumes · SSO/SCIM · OEM builder SDK",
|
|
26
|
+
contactEmail: "contact@fluidgrids.com"
|
|
27
|
+
},
|
|
28
|
+
vibecontrols: {
|
|
29
|
+
highlights: [
|
|
30
|
+
"Everything in Pro",
|
|
31
|
+
"Unlimited agents & seats",
|
|
32
|
+
"Production SSO/SCIM · SIEM streaming",
|
|
33
|
+
"Data residency · customer-managed keys",
|
|
34
|
+
"Dedicated tenant / self-host",
|
|
35
|
+
"Custom SLA · dedicated support"
|
|
36
|
+
],
|
|
37
|
+
summary: "Dedicated tenant · SSO/SCIM · Custom SLA",
|
|
38
|
+
contactEmail: "contact@vibecontrols.com"
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
function n(n) {
|
|
42
|
+
return n ? t[n.toLowerCase()] ?? e : e;
|
|
43
|
+
}
|
|
44
|
+
function r(e) {
|
|
45
|
+
return `mailto:${e.contactEmail}?subject=Enterprise%20Plan%20Inquiry`;
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { r as enterpriseMailto, n as getEnterpriseOffer };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=enterprise.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enterprise.js","names":[],"sources":["../../../../../src/billing/modules/plans/constants/enterprise.ts"],"sourcesContent":["/**\n * Enterprise \"Contact Us\" copy for the plans browse page.\n *\n * Enterprise is contact-sales in every product and has no backend plan\n * record, so its card is static. The highlights are product-specific and must\n * match the product website's Enterprise card, so a FluidGrids shell never\n * shows VibeControls wording (and vice versa). Products without curated copy\n * fall back to product-neutral highlights.\n */\n\nexport interface EnterpriseOffer {\n /** Bullet points on the grid card, in display order. */\n highlights: readonly string[];\n /** One-line summary for the compact list row. */\n summary: string;\n /** Sales inbox the Contact Us button opens. */\n contactEmail: string;\n}\n\nconst DEFAULT_ENTERPRISE_OFFER: EnterpriseOffer = {\n highlights: [\n 'Custom usage volumes',\n 'SSO / SCIM provisioning',\n 'Custom SLA & support contract',\n 'Volume-based custom pricing',\n 'Audit log with custom retention',\n 'Priority onboarding & migration',\n ],\n summary: 'SSO/SCIM · Custom SLA · Custom pricing',\n contactEmail: 'contact@burdenoff.com',\n};\n\n/** Keyed by billing product id. Mirrors each product website's Enterprise card. */\nconst ENTERPRISE_OFFERS: Readonly<Record<string, EnterpriseOffer>> = {\n // fluidgrids-website src/config/product.ts (plan key \"enterprise\")\n fluidgrids: {\n highlights: [\n 'Custom workflow and run volumes',\n 'Custom storage and retention',\n 'SSO & SAML',\n 'SCIM provisioning',\n '99.95%+ uptime SLA',\n 'Dedicated support with SLA',\n 'OEM embedding of the builder SDK',\n 'Data residency options',\n ],\n summary: 'Custom volumes · SSO/SCIM · OEM builder SDK',\n contactEmail: 'contact@fluidgrids.com',\n },\n // vibecontrols-website src/config/product.ts VC_ENTERPRISE_FEATURES\n vibecontrols: {\n highlights: [\n 'Everything in Pro',\n 'Unlimited agents & seats',\n 'Production SSO/SCIM · SIEM streaming',\n 'Data residency · customer-managed keys',\n 'Dedicated tenant / self-host',\n 'Custom SLA · dedicated support',\n ],\n summary: 'Dedicated tenant · SSO/SCIM · Custom SLA',\n contactEmail: 'contact@vibecontrols.com',\n },\n};\n\nexport function getEnterpriseOffer(productId: string | null | undefined): EnterpriseOffer {\n if (!productId) return DEFAULT_ENTERPRISE_OFFER;\n return ENTERPRISE_OFFERS[productId.toLowerCase()] ?? DEFAULT_ENTERPRISE_OFFER;\n}\n\nexport function enterpriseMailto(offer: EnterpriseOffer): string {\n return `mailto:${offer.contactEmail}?subject=${encodeURIComponent('Enterprise Plan Inquiry')}`;\n}\n"],"mappings":";AAmBA,IAAM,IAA4C;CAChD,YAAY;EACV;EACA;EACA;EACA;EACA;EACA;CACF;CACA,SAAS;CACT,cAAc;AAChB,GAGM,IAA+D;CAEnE,YAAY;EACV,YAAY;GACV;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,SAAS;EACT,cAAc;CAChB;CAEA,cAAc;EACZ,YAAY;GACV;GACA;GACA;GACA;GACA;GACA;EACF;EACA,SAAS;EACT,cAAc;CAChB;AACF;AAEA,SAAgB,EAAmB,GAAuD;CAExF,OADK,IACE,EAAkB,EAAU,YAAY,MAAM,IAD9B;AAEzB;AAEA,SAAgB,EAAiB,GAAgC;CAC/D,OAAO,UAAU,EAAM,aAAa;AACtC"}
|
|
@@ -11,13 +11,14 @@ import { getDisplayPrice as l, useDefaultBillingCurrency as u } from "../../../h
|
|
|
11
11
|
import { usePlans as d } from "../hooks/usePlans.js";
|
|
12
12
|
import "../hooks/index.js";
|
|
13
13
|
import { FEATURED_QUOTA_LABELS as f, FEATURED_QUOTA_NAMES as p, formatFeaturedQuotaValue as m } from "../constants/featuredQuotas.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
14
|
+
import { enterpriseMailto as h, getEnterpriseOffer as g } from "../constants/enterprise.js";
|
|
15
|
+
import { useState as _ } from "react";
|
|
16
|
+
import { ArrowRight as v, Check as y, Sparkles as b } from "lucide-react";
|
|
17
|
+
import { jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
18
|
+
import { useI18n as C } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
19
|
+
import { IllustratedEmptyState as w, PagePurpose as T } from "@burdenoff/fe-libs/ui";
|
|
19
20
|
//#region src/billing/modules/plans/pages/PlansBrowsePage.tsx
|
|
20
|
-
function
|
|
21
|
+
function E(e) {
|
|
21
22
|
let t = /* @__PURE__ */ new Map();
|
|
22
23
|
for (let n of e) {
|
|
23
24
|
if (!n.quota) continue;
|
|
@@ -30,110 +31,112 @@ function w(e) {
|
|
|
30
31
|
}
|
|
31
32
|
return Array.from(t.values());
|
|
32
33
|
}
|
|
33
|
-
function
|
|
34
|
-
let t =
|
|
34
|
+
function D(e) {
|
|
35
|
+
let t = E(e), n = new Map(t.map((e) => [e.name, e]));
|
|
35
36
|
return p.flatMap((e) => {
|
|
36
37
|
let t = n.get(e);
|
|
37
38
|
return t ? [t] : [];
|
|
38
39
|
});
|
|
39
40
|
}
|
|
40
|
-
var
|
|
41
|
-
let { t: r } =
|
|
41
|
+
var O = () => {
|
|
42
|
+
let { t: r } = C(), i = (e, t) => {
|
|
42
43
|
let n = r(e);
|
|
43
44
|
return n === e ? t : n;
|
|
44
|
-
}, a = c(), { productId: s } = e(), { plans:
|
|
45
|
+
}, a = c(), { productId: s, plansDocsUrl: l } = e(), u = g(s), { plans: f, isLoading: p, error: m, refetch: h } = d({
|
|
45
46
|
includeFree: !1,
|
|
46
47
|
productId: s ?? void 0
|
|
47
|
-
}), [
|
|
48
|
-
return
|
|
48
|
+
}), [v, y] = _("grid"), [b, E] = _(n.MONTHLY), D = f.filter((e) => e.isActive && e.isPurchasable !== !1 && e.duration === b), O = D.length >= 2 ? 1 : -1;
|
|
49
|
+
return p && f.length === 0 ? /* @__PURE__ */ S("div", {
|
|
49
50
|
className: "space-y-6 px-6 lg:px-8 py-6",
|
|
50
|
-
children: [/* @__PURE__ */
|
|
51
|
+
children: [/* @__PURE__ */ x("div", { className: "h-8 w-48 bg-bg-sunken animate-pulse rounded-card" }), /* @__PURE__ */ x("div", {
|
|
51
52
|
className: "grid grid-cols-1 md:grid-cols-3 gap-6",
|
|
52
53
|
children: [
|
|
53
54
|
1,
|
|
54
55
|
2,
|
|
55
56
|
3
|
|
56
|
-
].map((e) => /* @__PURE__ */
|
|
57
|
+
].map((e) => /* @__PURE__ */ S("div", {
|
|
57
58
|
className: "border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 p-5 space-y-4",
|
|
58
59
|
children: [
|
|
59
|
-
/* @__PURE__ */
|
|
60
|
-
/* @__PURE__ */
|
|
61
|
-
/* @__PURE__ */
|
|
60
|
+
/* @__PURE__ */ x("div", { className: "h-6 w-24 bg-bg-sunken animate-pulse rounded" }),
|
|
61
|
+
/* @__PURE__ */ x("div", { className: "h-8 w-32 bg-bg-sunken animate-pulse rounded" }),
|
|
62
|
+
/* @__PURE__ */ x("div", {
|
|
62
63
|
className: "space-y-2",
|
|
63
64
|
children: [
|
|
64
65
|
1,
|
|
65
66
|
2,
|
|
66
67
|
3
|
|
67
|
-
].map((e) => /* @__PURE__ */
|
|
68
|
+
].map((e) => /* @__PURE__ */ x("div", { className: "h-4 w-full bg-bg-sunken animate-pulse rounded" }, e))
|
|
68
69
|
})
|
|
69
70
|
]
|
|
70
71
|
}, e))
|
|
71
72
|
})]
|
|
72
|
-
}) :
|
|
73
|
+
}) : m && o(m) ? /* @__PURE__ */ x("div", {
|
|
73
74
|
className: "p-6",
|
|
74
|
-
children: /* @__PURE__ */
|
|
75
|
+
children: /* @__PURE__ */ x(t, {
|
|
75
76
|
title: i("billing.plans.serverUnavailable", "Server Unavailable"),
|
|
76
77
|
message: i("billing.plans.unableToLoadPlans", "Unable to load plans."),
|
|
77
|
-
onRetry: () =>
|
|
78
|
+
onRetry: () => h(),
|
|
78
79
|
showRetry: !0
|
|
79
80
|
})
|
|
80
|
-
}) : /* @__PURE__ */
|
|
81
|
+
}) : /* @__PURE__ */ S("div", {
|
|
81
82
|
className: "space-y-6 px-6 lg:px-8 py-6",
|
|
82
83
|
children: [
|
|
83
|
-
/* @__PURE__ */
|
|
84
|
-
/* @__PURE__ */
|
|
84
|
+
/* @__PURE__ */ S("div", { children: [
|
|
85
|
+
/* @__PURE__ */ x("h1", {
|
|
85
86
|
className: "text-2xl md:text-3xl font-semibold tracking-tight text-text-primary",
|
|
86
87
|
children: i("billing.plans.browsePlansTitle", "Plans")
|
|
87
88
|
}),
|
|
88
|
-
/* @__PURE__ */
|
|
89
|
+
/* @__PURE__ */ x("p", {
|
|
89
90
|
className: "text-sm text-text-secondary mt-1",
|
|
90
91
|
children: i("billing.plans.browsePlansSubtitle", "Choose a plan that fits your needs")
|
|
91
92
|
}),
|
|
92
|
-
/* @__PURE__ */
|
|
93
|
+
/* @__PURE__ */ x(T, {
|
|
93
94
|
className: "mt-3",
|
|
95
|
+
learnMoreHref: l,
|
|
96
|
+
learnMoreLabel: i("billing.plans.learnMore", "Learn more"),
|
|
94
97
|
children: i("billing.plans.browsePurpose", "Compare what each plan includes — quotas, features and price — and subscribe to the one that fits your team. Switch between monthly and yearly billing to see the difference, or contact us for a custom Enterprise contract.")
|
|
95
98
|
})
|
|
96
99
|
] }),
|
|
97
|
-
/* @__PURE__ */
|
|
100
|
+
/* @__PURE__ */ S("div", {
|
|
98
101
|
className: "flex flex-wrap items-center gap-4 pb-4 border-b border-border-subtle",
|
|
99
|
-
children: [/* @__PURE__ */
|
|
102
|
+
children: [/* @__PURE__ */ x("div", {
|
|
100
103
|
className: "inline-flex rounded-lg border border-border-subtle p-1 bg-bg-sunken",
|
|
101
|
-
children: [n.MONTHLY, n.YEARLY].map((e) => /* @__PURE__ */
|
|
104
|
+
children: [n.MONTHLY, n.YEARLY].map((e) => /* @__PURE__ */ x("button", {
|
|
102
105
|
type: "button",
|
|
103
|
-
onClick: () =>
|
|
104
|
-
className: `px-3 py-1.5 text-sm font-medium rounded-button transition-colors ${
|
|
106
|
+
onClick: () => E(e),
|
|
107
|
+
className: `px-3 py-1.5 text-sm font-medium rounded-button transition-colors ${b === e ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-secondary hover:text-text-primary"}`,
|
|
105
108
|
children: e === n.MONTHLY ? "Monthly" : "Yearly"
|
|
106
109
|
}, e))
|
|
107
|
-
}), /* @__PURE__ */
|
|
110
|
+
}), /* @__PURE__ */ S("div", {
|
|
108
111
|
className: "ml-auto inline-flex rounded-lg border border-border-subtle p-1 bg-bg-sunken",
|
|
109
|
-
children: [/* @__PURE__ */
|
|
112
|
+
children: [/* @__PURE__ */ x("button", {
|
|
110
113
|
type: "button",
|
|
111
|
-
onClick: () =>
|
|
112
|
-
className: `p-1.5 rounded-button transition-colors ${
|
|
114
|
+
onClick: () => y("grid"),
|
|
115
|
+
className: `p-1.5 rounded-button transition-colors ${v === "grid" ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-secondary hover:text-text-primary"}`,
|
|
113
116
|
title: "Grid view",
|
|
114
|
-
children: /* @__PURE__ */
|
|
117
|
+
children: /* @__PURE__ */ x("svg", {
|
|
115
118
|
className: "size-4",
|
|
116
119
|
fill: "none",
|
|
117
120
|
viewBox: "0 0 24 24",
|
|
118
121
|
stroke: "currentColor",
|
|
119
|
-
children: /* @__PURE__ */
|
|
122
|
+
children: /* @__PURE__ */ x("path", {
|
|
120
123
|
strokeLinecap: "round",
|
|
121
124
|
strokeLinejoin: "round",
|
|
122
125
|
strokeWidth: 2,
|
|
123
126
|
d: "M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"
|
|
124
127
|
})
|
|
125
128
|
})
|
|
126
|
-
}), /* @__PURE__ */
|
|
129
|
+
}), /* @__PURE__ */ x("button", {
|
|
127
130
|
type: "button",
|
|
128
|
-
onClick: () =>
|
|
129
|
-
className: `p-1.5 rounded-button transition-colors ${
|
|
131
|
+
onClick: () => y("list"),
|
|
132
|
+
className: `p-1.5 rounded-button transition-colors ${v === "list" ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-secondary hover:text-text-primary"}`,
|
|
130
133
|
title: "List view",
|
|
131
|
-
children: /* @__PURE__ */
|
|
134
|
+
children: /* @__PURE__ */ x("svg", {
|
|
132
135
|
className: "size-4",
|
|
133
136
|
fill: "none",
|
|
134
137
|
viewBox: "0 0 24 24",
|
|
135
138
|
stroke: "currentColor",
|
|
136
|
-
children: /* @__PURE__ */
|
|
139
|
+
children: /* @__PURE__ */ x("path", {
|
|
137
140
|
strokeLinecap: "round",
|
|
138
141
|
strokeLinejoin: "round",
|
|
139
142
|
strokeWidth: 2,
|
|
@@ -143,104 +146,95 @@ var E = () => {
|
|
|
143
146
|
})]
|
|
144
147
|
})]
|
|
145
148
|
}),
|
|
146
|
-
|
|
149
|
+
D.length === 0 ? /* @__PURE__ */ x(w, {
|
|
147
150
|
illustration: "empty-data",
|
|
148
151
|
title: i("billing.plans.noPlansAvailable", "No plans available"),
|
|
149
152
|
description: i("billing.plans.checkBackLater", "Check back later for new plans.")
|
|
150
|
-
}) :
|
|
153
|
+
}) : v === "grid" ? /* @__PURE__ */ S("div", {
|
|
151
154
|
className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 items-stretch",
|
|
152
|
-
children: [
|
|
155
|
+
children: [D.map((e, t) => /* @__PURE__ */ x(j, {
|
|
153
156
|
plan: e,
|
|
154
|
-
highlighted: t ===
|
|
157
|
+
highlighted: t === O,
|
|
155
158
|
onView: () => a(`/plans/${e.id}`),
|
|
156
159
|
onSubscribe: () => a(`/checkout/${e.id}`)
|
|
157
|
-
}, e.id)), /* @__PURE__ */
|
|
158
|
-
}) : /* @__PURE__ */
|
|
160
|
+
}, e.id)), /* @__PURE__ */ x(k, { offer: u })]
|
|
161
|
+
}) : /* @__PURE__ */ S("div", {
|
|
159
162
|
className: "space-y-3",
|
|
160
|
-
children: [
|
|
163
|
+
children: [D.map((e) => /* @__PURE__ */ x(M, {
|
|
161
164
|
plan: e,
|
|
162
165
|
onView: () => a(`/plans/${e.id}`),
|
|
163
166
|
onSubscribe: () => a(`/checkout/${e.id}`)
|
|
164
|
-
}, e.id)), /* @__PURE__ */
|
|
167
|
+
}, e.id)), /* @__PURE__ */ x(A, { offer: u })]
|
|
165
168
|
})
|
|
166
169
|
]
|
|
167
170
|
});
|
|
168
|
-
},
|
|
169
|
-
"Unlimited Agents & Sessions",
|
|
170
|
-
"Dedicated single-tenant infra",
|
|
171
|
-
"Customer-managed encryption keys",
|
|
172
|
-
"SSO / SCIM provisioning",
|
|
173
|
-
"Custom SLA & support contract",
|
|
174
|
-
"Volume-based custom pricing",
|
|
175
|
-
"Audit log with custom retention",
|
|
176
|
-
"Priority onboarding & migration"
|
|
177
|
-
], O = () => /* @__PURE__ */ b("div", {
|
|
171
|
+
}, k = ({ offer: e }) => /* @__PURE__ */ S("div", {
|
|
178
172
|
className: "border border-border-strong bg-[var(--color-accent-soft)] rounded-card shadow-elevation-1 p-5 flex flex-col h-full",
|
|
179
173
|
children: [
|
|
180
|
-
/* @__PURE__ */
|
|
174
|
+
/* @__PURE__ */ S("div", {
|
|
181
175
|
className: "mb-4",
|
|
182
|
-
children: [/* @__PURE__ */
|
|
176
|
+
children: [/* @__PURE__ */ S("div", {
|
|
183
177
|
className: "flex items-center gap-2",
|
|
184
|
-
children: [/* @__PURE__ */
|
|
178
|
+
children: [/* @__PURE__ */ x("h3", {
|
|
185
179
|
className: "text-lg font-semibold text-text-primary",
|
|
186
180
|
children: "Enterprise"
|
|
187
|
-
}), /* @__PURE__ */
|
|
181
|
+
}), /* @__PURE__ */ x("span", {
|
|
188
182
|
className: "text-xs font-medium px-2 py-0.5 rounded-full bg-bg-surface text-text-link",
|
|
189
183
|
children: "Contact Us"
|
|
190
184
|
})]
|
|
191
|
-
}), /* @__PURE__ */
|
|
185
|
+
}), /* @__PURE__ */ x("span", {
|
|
192
186
|
className: "text-sm text-text-secondary",
|
|
193
187
|
children: "Custom contract"
|
|
194
188
|
})]
|
|
195
189
|
}),
|
|
196
|
-
/* @__PURE__ */
|
|
190
|
+
/* @__PURE__ */ x("div", {
|
|
197
191
|
className: "flex items-baseline gap-1 mb-4",
|
|
198
|
-
children: /* @__PURE__ */
|
|
192
|
+
children: /* @__PURE__ */ x("span", {
|
|
199
193
|
className: "text-2xl font-semibold text-text-primary",
|
|
200
194
|
children: "Custom pricing"
|
|
201
195
|
})
|
|
202
196
|
}),
|
|
203
|
-
/* @__PURE__ */
|
|
197
|
+
/* @__PURE__ */ x("ul", {
|
|
204
198
|
className: "space-y-2 mb-4 flex-1",
|
|
205
|
-
children:
|
|
199
|
+
children: e.highlights.map((e) => /* @__PURE__ */ S("li", {
|
|
206
200
|
className: "flex items-center gap-2 text-sm",
|
|
207
|
-
children: [/* @__PURE__ */
|
|
201
|
+
children: [/* @__PURE__ */ x("svg", {
|
|
208
202
|
className: "size-4 text-status-success-text shrink-0",
|
|
209
203
|
fill: "none",
|
|
210
204
|
viewBox: "0 0 24 24",
|
|
211
205
|
stroke: "currentColor",
|
|
212
|
-
children: /* @__PURE__ */
|
|
206
|
+
children: /* @__PURE__ */ x("path", {
|
|
213
207
|
strokeLinecap: "round",
|
|
214
208
|
strokeLinejoin: "round",
|
|
215
209
|
strokeWidth: 2,
|
|
216
210
|
d: "M5 13l4 4L19 7"
|
|
217
211
|
})
|
|
218
|
-
}), /* @__PURE__ */
|
|
212
|
+
}), /* @__PURE__ */ x("span", {
|
|
219
213
|
className: "text-text-primary",
|
|
220
214
|
children: e
|
|
221
215
|
})]
|
|
222
216
|
}, e))
|
|
223
217
|
}),
|
|
224
|
-
/* @__PURE__ */
|
|
218
|
+
/* @__PURE__ */ x("div", {
|
|
225
219
|
className: "space-y-2 pt-4 border-t border-border-subtle mt-auto",
|
|
226
|
-
children: /* @__PURE__ */
|
|
227
|
-
href:
|
|
220
|
+
children: /* @__PURE__ */ x("a", {
|
|
221
|
+
href: h(e),
|
|
228
222
|
className: "block w-full px-4 py-2 text-sm font-medium text-center bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors duration-200",
|
|
229
223
|
children: "Contact Us"
|
|
230
224
|
})
|
|
231
225
|
})
|
|
232
226
|
]
|
|
233
|
-
}),
|
|
227
|
+
}), A = ({ offer: e }) => /* @__PURE__ */ S("div", {
|
|
234
228
|
className: "flex items-center gap-4 p-5 border border-border-strong rounded-card bg-[var(--color-accent-soft)] shadow-elevation-1 transition-all duration-200 hover:shadow-elevation-2",
|
|
235
229
|
children: [
|
|
236
|
-
/* @__PURE__ */
|
|
230
|
+
/* @__PURE__ */ x("div", {
|
|
237
231
|
className: "size-10 rounded-lg bg-bg-surface flex items-center justify-center shrink-0 shadow-elevation-1",
|
|
238
|
-
children: /* @__PURE__ */
|
|
232
|
+
children: /* @__PURE__ */ x("svg", {
|
|
239
233
|
className: "size-5 text-text-link",
|
|
240
234
|
fill: "none",
|
|
241
235
|
viewBox: "0 0 24 24",
|
|
242
236
|
stroke: "currentColor",
|
|
243
|
-
children: /* @__PURE__ */
|
|
237
|
+
children: /* @__PURE__ */ x("path", {
|
|
244
238
|
strokeLinecap: "round",
|
|
245
239
|
strokeLinejoin: "round",
|
|
246
240
|
strokeWidth: 2,
|
|
@@ -248,91 +242,91 @@ var E = () => {
|
|
|
248
242
|
})
|
|
249
243
|
})
|
|
250
244
|
}),
|
|
251
|
-
/* @__PURE__ */
|
|
245
|
+
/* @__PURE__ */ S("div", {
|
|
252
246
|
className: "flex-1 min-w-0",
|
|
253
|
-
children: [/* @__PURE__ */
|
|
247
|
+
children: [/* @__PURE__ */ S("div", {
|
|
254
248
|
className: "flex items-center gap-2",
|
|
255
|
-
children: [/* @__PURE__ */
|
|
249
|
+
children: [/* @__PURE__ */ x("h3", {
|
|
256
250
|
className: "font-semibold text-text-primary",
|
|
257
251
|
children: "Enterprise"
|
|
258
|
-
}), /* @__PURE__ */
|
|
252
|
+
}), /* @__PURE__ */ x("span", {
|
|
259
253
|
className: "text-xs font-medium px-2 py-0.5 rounded-full bg-bg-surface text-text-link shrink-0",
|
|
260
254
|
children: "Contact Us"
|
|
261
255
|
})]
|
|
262
|
-
}), /* @__PURE__ */
|
|
256
|
+
}), /* @__PURE__ */ x("p", {
|
|
263
257
|
className: "text-sm text-text-secondary",
|
|
264
|
-
children:
|
|
258
|
+
children: e.summary
|
|
265
259
|
})]
|
|
266
260
|
}),
|
|
267
|
-
/* @__PURE__ */
|
|
261
|
+
/* @__PURE__ */ S("div", {
|
|
268
262
|
className: "text-right",
|
|
269
|
-
children: [/* @__PURE__ */
|
|
263
|
+
children: [/* @__PURE__ */ x("div", {
|
|
270
264
|
className: "font-semibold text-text-primary",
|
|
271
265
|
children: "Custom pricing"
|
|
272
|
-
}), /* @__PURE__ */
|
|
266
|
+
}), /* @__PURE__ */ x("div", {
|
|
273
267
|
className: "text-sm text-text-secondary",
|
|
274
268
|
children: "contract-based"
|
|
275
269
|
})]
|
|
276
270
|
}),
|
|
277
|
-
/* @__PURE__ */
|
|
278
|
-
href:
|
|
271
|
+
/* @__PURE__ */ x("a", {
|
|
272
|
+
href: h(e),
|
|
279
273
|
className: "px-3 py-1.5 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors duration-200 shrink-0",
|
|
280
274
|
children: "Contact Us"
|
|
281
275
|
})
|
|
282
276
|
]
|
|
283
|
-
}),
|
|
284
|
-
let s =
|
|
285
|
-
return /* @__PURE__ */
|
|
277
|
+
}), j = ({ plan: e, highlighted: t = !1, onView: n, onSubscribe: o }) => {
|
|
278
|
+
let s = D(e.features || []), c = E(e.features || []).length - s.length, d = u(), { price: p, currency: h } = l(e.price, e.currency, e.currencyPrices, d);
|
|
279
|
+
return /* @__PURE__ */ S("div", {
|
|
286
280
|
className: `relative flex flex-col h-full rounded-card bg-bg-surface p-6 transition-[border-color,box-shadow] duration-200 ${t ? "border-2 border-action-primary-bg shadow-[var(--shadow-glow)]" : "border border-border-subtle shadow-elevation-1 hover:border-border-strong hover:shadow-elevation-2"}`,
|
|
287
281
|
children: [
|
|
288
|
-
t && /* @__PURE__ */
|
|
282
|
+
t && /* @__PURE__ */ S("span", {
|
|
289
283
|
className: "absolute -top-3 left-1/2 -translate-x-1/2 inline-flex items-center gap-1 rounded-full bg-action-primary-bg px-3 py-1 text-xs font-semibold text-action-primary-text shadow-elevation-2",
|
|
290
|
-
children: [/* @__PURE__ */
|
|
284
|
+
children: [/* @__PURE__ */ x(b, {
|
|
291
285
|
className: "size-3.5",
|
|
292
286
|
"aria-hidden": "true"
|
|
293
287
|
}), "Most popular"]
|
|
294
288
|
}),
|
|
295
|
-
/* @__PURE__ */
|
|
289
|
+
/* @__PURE__ */ S("div", {
|
|
296
290
|
className: "mb-4",
|
|
297
|
-
children: [/* @__PURE__ */
|
|
291
|
+
children: [/* @__PURE__ */ x("h3", {
|
|
298
292
|
className: "text-lg font-semibold text-text-primary",
|
|
299
293
|
children: e.name
|
|
300
|
-
}), /* @__PURE__ */
|
|
294
|
+
}), /* @__PURE__ */ x("span", {
|
|
301
295
|
className: "text-sm text-text-secondary",
|
|
302
296
|
children: a(e.duration)
|
|
303
297
|
})]
|
|
304
298
|
}),
|
|
305
|
-
/* @__PURE__ */
|
|
299
|
+
/* @__PURE__ */ S("div", {
|
|
306
300
|
className: "flex items-baseline gap-1.5 mb-5",
|
|
307
|
-
children: [/* @__PURE__ */
|
|
301
|
+
children: [/* @__PURE__ */ x("span", {
|
|
308
302
|
className: "text-4xl font-bold tabular-nums tracking-tight text-text-primary",
|
|
309
303
|
children: r(p, h)
|
|
310
|
-
}), /* @__PURE__ */
|
|
304
|
+
}), /* @__PURE__ */ S("span", {
|
|
311
305
|
className: "text-text-secondary text-sm",
|
|
312
306
|
children: ["/ ", i(e.duration)]
|
|
313
307
|
})]
|
|
314
308
|
}),
|
|
315
|
-
s.length > 0 && /* @__PURE__ */
|
|
309
|
+
s.length > 0 && /* @__PURE__ */ S("ul", {
|
|
316
310
|
className: "space-y-2.5 mb-5 flex-1",
|
|
317
|
-
children: [s.map((e) => /* @__PURE__ */
|
|
311
|
+
children: [s.map((e) => /* @__PURE__ */ S("li", {
|
|
318
312
|
className: "flex items-center justify-between gap-2 text-sm",
|
|
319
|
-
children: [/* @__PURE__ */
|
|
313
|
+
children: [/* @__PURE__ */ S("span", {
|
|
320
314
|
className: "flex items-center gap-2 min-w-0",
|
|
321
|
-
children: [/* @__PURE__ */
|
|
315
|
+
children: [/* @__PURE__ */ x("span", {
|
|
322
316
|
className: "flex size-5 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)]",
|
|
323
|
-
children: /* @__PURE__ */ y
|
|
317
|
+
children: /* @__PURE__ */ x(y, {
|
|
324
318
|
className: "size-3 text-action-primary-bg",
|
|
325
319
|
"aria-hidden": "true"
|
|
326
320
|
})
|
|
327
|
-
}), /* @__PURE__ */
|
|
321
|
+
}), /* @__PURE__ */ x("span", {
|
|
328
322
|
className: "truncate text-text-primary",
|
|
329
323
|
children: f[e.name] ?? e.name
|
|
330
324
|
})]
|
|
331
|
-
}), /* @__PURE__ */
|
|
325
|
+
}), /* @__PURE__ */ x("span", {
|
|
332
326
|
className: "font-semibold text-text-primary tabular-nums shrink-0",
|
|
333
327
|
children: m(e.name, e.totalValue)
|
|
334
328
|
})]
|
|
335
|
-
}, e.quotaId)), c > 0 && /* @__PURE__ */
|
|
329
|
+
}, e.quotaId)), c > 0 && /* @__PURE__ */ S("li", {
|
|
336
330
|
className: "text-sm text-text-secondary pl-7",
|
|
337
331
|
children: [
|
|
338
332
|
"+",
|
|
@@ -341,17 +335,17 @@ var E = () => {
|
|
|
341
335
|
]
|
|
342
336
|
})]
|
|
343
337
|
}),
|
|
344
|
-
/* @__PURE__ */
|
|
338
|
+
/* @__PURE__ */ S("div", {
|
|
345
339
|
className: "space-y-2 pt-4 border-t border-border-subtle mt-auto",
|
|
346
|
-
children: [/* @__PURE__ */
|
|
340
|
+
children: [/* @__PURE__ */ S("button", {
|
|
347
341
|
type: "button",
|
|
348
342
|
onClick: o,
|
|
349
343
|
className: "group inline-flex w-full items-center justify-center gap-2 rounded-button bg-action-primary-bg px-4 py-2.5 text-sm font-semibold text-action-primary-text shadow-elevation-1 transition-colors duration-200 hover:bg-action-primary-bgHover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
|
|
350
|
-
children: ["Subscribe", /* @__PURE__ */
|
|
344
|
+
children: ["Subscribe", /* @__PURE__ */ x(v, {
|
|
351
345
|
className: "size-4 transition-transform duration-200",
|
|
352
346
|
"aria-hidden": "true"
|
|
353
347
|
})]
|
|
354
|
-
}), /* @__PURE__ */
|
|
348
|
+
}), /* @__PURE__ */ x("button", {
|
|
355
349
|
type: "button",
|
|
356
350
|
onClick: n,
|
|
357
351
|
className: "w-full px-4 py-2 text-sm font-medium border border-border-subtle text-text-secondary rounded-button hover:bg-bg-sunken hover:text-text-primary hover:border-border-strong transition-colors duration-200",
|
|
@@ -360,19 +354,19 @@ var E = () => {
|
|
|
360
354
|
})
|
|
361
355
|
]
|
|
362
356
|
});
|
|
363
|
-
},
|
|
364
|
-
let a =
|
|
365
|
-
return /* @__PURE__ */
|
|
357
|
+
}, M = ({ plan: e, onView: t, onSubscribe: n }) => {
|
|
358
|
+
let a = D(e.features || []), o = u();
|
|
359
|
+
return /* @__PURE__ */ S("div", {
|
|
366
360
|
className: "flex items-center gap-4 p-5 border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 transition-all duration-200 hover:border-border-strong hover:shadow-elevation-2",
|
|
367
361
|
children: [
|
|
368
|
-
/* @__PURE__ */
|
|
362
|
+
/* @__PURE__ */ x("div", {
|
|
369
363
|
className: "size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center shrink-0",
|
|
370
|
-
children: /* @__PURE__ */
|
|
364
|
+
children: /* @__PURE__ */ x("svg", {
|
|
371
365
|
className: "size-5 text-text-link",
|
|
372
366
|
fill: "none",
|
|
373
367
|
viewBox: "0 0 24 24",
|
|
374
368
|
stroke: "currentColor",
|
|
375
|
-
children: /* @__PURE__ */
|
|
369
|
+
children: /* @__PURE__ */ x("path", {
|
|
376
370
|
strokeLinecap: "round",
|
|
377
371
|
strokeLinejoin: "round",
|
|
378
372
|
strokeWidth: 2,
|
|
@@ -380,12 +374,12 @@ var E = () => {
|
|
|
380
374
|
})
|
|
381
375
|
})
|
|
382
376
|
}),
|
|
383
|
-
/* @__PURE__ */
|
|
377
|
+
/* @__PURE__ */ S("div", {
|
|
384
378
|
className: "flex-1 min-w-0",
|
|
385
|
-
children: [/* @__PURE__ */
|
|
379
|
+
children: [/* @__PURE__ */ x("h3", {
|
|
386
380
|
className: "font-semibold text-text-primary truncate",
|
|
387
381
|
children: e.name
|
|
388
|
-
}), /* @__PURE__ */
|
|
382
|
+
}), /* @__PURE__ */ S("p", {
|
|
389
383
|
className: "text-sm text-text-secondary",
|
|
390
384
|
children: [
|
|
391
385
|
a.length,
|
|
@@ -395,27 +389,27 @@ var E = () => {
|
|
|
395
389
|
]
|
|
396
390
|
})]
|
|
397
391
|
}),
|
|
398
|
-
/* @__PURE__ */
|
|
392
|
+
/* @__PURE__ */ S("div", {
|
|
399
393
|
className: "text-right",
|
|
400
|
-
children: [/* @__PURE__ */
|
|
394
|
+
children: [/* @__PURE__ */ x("div", {
|
|
401
395
|
className: "font-semibold text-text-primary",
|
|
402
396
|
children: (() => {
|
|
403
397
|
let { price: t, currency: n } = l(e.price, e.currency, e.currencyPrices, o);
|
|
404
398
|
return r(t, n);
|
|
405
399
|
})()
|
|
406
|
-
}), /* @__PURE__ */
|
|
400
|
+
}), /* @__PURE__ */ S("div", {
|
|
407
401
|
className: "text-sm text-text-secondary",
|
|
408
402
|
children: ["per ", i(e.duration)]
|
|
409
403
|
})]
|
|
410
404
|
}),
|
|
411
|
-
/* @__PURE__ */
|
|
405
|
+
/* @__PURE__ */ S("div", {
|
|
412
406
|
className: "flex items-center gap-2",
|
|
413
|
-
children: [/* @__PURE__ */
|
|
407
|
+
children: [/* @__PURE__ */ x("button", {
|
|
414
408
|
type: "button",
|
|
415
409
|
onClick: n,
|
|
416
410
|
className: "px-3 py-1.5 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors duration-200",
|
|
417
411
|
children: "Subscribe"
|
|
418
|
-
}), /* @__PURE__ */
|
|
412
|
+
}), /* @__PURE__ */ x("button", {
|
|
419
413
|
type: "button",
|
|
420
414
|
onClick: t,
|
|
421
415
|
className: "px-3 py-1.5 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken hover:border-border-strong transition-all duration-200",
|
|
@@ -426,6 +420,6 @@ var E = () => {
|
|
|
426
420
|
});
|
|
427
421
|
};
|
|
428
422
|
//#endregion
|
|
429
|
-
export {
|
|
423
|
+
export { O as PlansBrowsePage };
|
|
430
424
|
|
|
431
425
|
//# sourceMappingURL=PlansBrowsePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlansBrowsePage.js","names":[],"sources":["../../../../../src/billing/modules/plans/pages/PlansBrowsePage.tsx"],"sourcesContent":["/**\n * Plans Module - Plans Browse Page (User-facing)\n * Browse available plans and subscribe. No create/edit/toggle actions.\n */\n\nimport { useState, type FC } from 'react';\nimport { Check, ArrowRight, Sparkles } from 'lucide-react';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { usePlans } from '../hooks';\nimport { ServerError } from '../../../shared/components';\nimport {\n formatCurrency,\n formatPlanDuration,\n formatPlanDurationLabel,\n getPlanFeatureQuantity,\n isServerError,\n} from '../../../shared/utils';\nimport { PlanDuration } from '../../../shared/types';\nimport type { Plan, PlanFeature } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { PagePurpose, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport {\n useDefaultBillingCurrency,\n getDisplayPrice,\n} from '../../../hooks/useDefaultBillingCurrency';\nimport {\n FEATURED_QUOTA_NAMES,\n FEATURED_QUOTA_LABELS,\n formatFeaturedQuotaValue,\n} from '../constants/featuredQuotas';\n\ntype ViewMode = 'grid' | 'list';\n\ninterface CombinedQuota {\n quotaId: string;\n name: string;\n totalValue: number;\n}\n\nfunction combineQuotas(features: PlanFeature[]): CombinedQuota[] {\n const quotaMap = new Map<string, CombinedQuota>();\n for (const feature of features) {\n if (!feature.quota) continue;\n const quotaId = feature.quota.id;\n const limitValue = getPlanFeatureQuantity(feature);\n const existing = quotaMap.get(quotaId);\n if (existing) {\n existing.totalValue += limitValue;\n } else {\n quotaMap.set(quotaId, {\n quotaId,\n name: feature.quota.name ?? 'Unknown Quota',\n totalValue: limitValue,\n });\n }\n }\n return Array.from(quotaMap.values());\n}\n\n/** Returns only the featured quotas, in the canonical display order. */\nfunction getFeaturedQuotas(features: PlanFeature[]): CombinedQuota[] {\n const all = combineQuotas(features);\n const byName = new Map(all.map((q) => [q.name, q]));\n return FEATURED_QUOTA_NAMES.flatMap((name) => {\n const q = byName.get(name);\n return q ? [q] : [];\n });\n}\n\nexport const PlansBrowsePage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const navigateTo = useBillingNavigate();\n const { productId } = useBilling();\n const { plans, isLoading, error, refetch } = usePlans({\n includeFree: false,\n productId: productId ?? undefined,\n });\n\n const [viewMode, setViewMode] = useState<ViewMode>('grid');\n const [billingInterval, setBillingInterval] = useState<PlanDuration>(PlanDuration.MONTHLY);\n\n // Only show active, publicly purchasable plans to users\n const filteredPlans = plans.filter(\n (plan) => plan.isActive && plan.isPurchasable !== false && plan.duration === billingInterval\n );\n\n // Spotlight a recommended tier (the second plan when there are 2+, the common\n // \"mid / Pro\" sweet-spot) so the eye lands on a clear primary choice.\n const recommendedIndex = filteredPlans.length >= 2 ? 1 : -1;\n\n if (isLoading && plans.length === 0) {\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n <div className=\"h-8 w-48 bg-bg-sunken animate-pulse rounded-card\" />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-6\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 p-5 space-y-4\"\n >\n <div className=\"h-6 w-24 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-8 w-32 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"space-y-2\">\n {[1, 2, 3].map((j) => (\n <div key={j} className=\"h-4 w-full bg-bg-sunken animate-pulse rounded\" />\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n if (error && isServerError(error)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title={tr('billing.plans.serverUnavailable', 'Server Unavailable')}\n message={tr('billing.plans.unableToLoadPlans', 'Unable to load plans.')}\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n {/* Header */}\n <div>\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n {tr('billing.plans.browsePlansTitle', 'Plans')}\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr('billing.plans.browsePlansSubtitle', 'Choose a plan that fits your needs')}\n </p>\n <PagePurpose className=\"mt-3\">\n {tr(\n 'billing.plans.browsePurpose',\n 'Compare what each plan includes — quotas, features and price — and subscribe to the one that fits your team. Switch between monthly and yearly billing to see the difference, or contact us for a custom Enterprise contract.'\n )}\n </PagePurpose>\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-wrap items-center gap-4 pb-4 border-b border-border-subtle\">\n <div className=\"inline-flex rounded-lg border border-border-subtle p-1 bg-bg-sunken\">\n {([PlanDuration.MONTHLY, PlanDuration.YEARLY] as const).map((interval) => (\n <button\n type=\"button\"\n key={interval}\n onClick={() => setBillingInterval(interval)}\n className={`px-3 py-1.5 text-sm font-medium rounded-button transition-colors ${\n billingInterval === interval\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n >\n {interval === PlanDuration.MONTHLY ? 'Monthly' : 'Yearly'}\n </button>\n ))}\n </div>\n\n <div className=\"ml-auto inline-flex rounded-lg border border-border-subtle p-1 bg-bg-sunken\">\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`p-1.5 rounded-button transition-colors ${\n viewMode === 'grid'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n title=\"Grid view\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`p-1.5 rounded-button transition-colors ${\n viewMode === 'list'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n title=\"List view\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 6h16M4 12h16M4 18h16\"\n />\n </svg>\n </button>\n </div>\n </div>\n\n {/* Plans */}\n {filteredPlans.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={tr('billing.plans.noPlansAvailable', 'No plans available')}\n description={tr('billing.plans.checkBackLater', 'Check back later for new plans.')}\n />\n ) : viewMode === 'grid' ? (\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 items-stretch\">\n {filteredPlans.map((plan, idx) => (\n <BrowsePlanCard\n key={plan.id}\n plan={plan}\n highlighted={idx === recommendedIndex}\n onView={() => navigateTo(`/plans/${plan.id}`)}\n onSubscribe={() => navigateTo(`/checkout/${plan.id}`)}\n />\n ))}\n <EnterpriseCard />\n </div>\n ) : (\n <div className=\"space-y-3\">\n {filteredPlans.map((plan) => (\n <BrowsePlanRow\n key={plan.id}\n plan={plan}\n onView={() => navigateTo(`/plans/${plan.id}`)}\n onSubscribe={() => navigateTo(`/checkout/${plan.id}`)}\n />\n ))}\n <EnterpriseRow />\n </div>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Static Enterprise \"Contact Us\" card — no backend plan record\n// ============================================================================\n\nconst ENTERPRISE_HIGHLIGHTS = [\n 'Unlimited Agents & Sessions',\n 'Dedicated single-tenant infra',\n 'Customer-managed encryption keys',\n 'SSO / SCIM provisioning',\n 'Custom SLA & support contract',\n 'Volume-based custom pricing',\n 'Audit log with custom retention',\n 'Priority onboarding & migration',\n];\n\nconst EnterpriseCard: FC = () => (\n <div className=\"border border-border-strong bg-[var(--color-accent-soft)] rounded-card shadow-elevation-1 p-5 flex flex-col h-full\">\n <div className=\"mb-4\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Enterprise</h3>\n <span className=\"text-xs font-medium px-2 py-0.5 rounded-full bg-bg-surface text-text-link\">\n Contact Us\n </span>\n </div>\n <span className=\"text-sm text-text-secondary\">Custom contract</span>\n </div>\n\n <div className=\"flex items-baseline gap-1 mb-4\">\n <span className=\"text-2xl font-semibold text-text-primary\">Custom pricing</span>\n </div>\n\n <ul className=\"space-y-2 mb-4 flex-1\">\n {ENTERPRISE_HIGHLIGHTS.map((item) => (\n <li key={item} className=\"flex items-center gap-2 text-sm\">\n <svg\n className=\"size-4 text-status-success-text shrink-0\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M5 13l4 4L19 7\" />\n </svg>\n <span className=\"text-text-primary\">{item}</span>\n </li>\n ))}\n </ul>\n\n <div className=\"space-y-2 pt-4 border-t border-border-subtle mt-auto\">\n <a\n href=\"mailto:contact@burdenoff.com?subject=Enterprise Plan Inquiry\"\n className=\"block w-full px-4 py-2 text-sm font-medium text-center bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors duration-200\"\n >\n Contact Us\n </a>\n </div>\n </div>\n);\n\nconst EnterpriseRow: FC = () => (\n <div className=\"flex items-center gap-4 p-5 border border-border-strong rounded-card bg-[var(--color-accent-soft)] shadow-elevation-1 transition-all duration-200 hover:shadow-elevation-2\">\n <div className=\"size-10 rounded-lg bg-bg-surface flex items-center justify-center shrink-0 shadow-elevation-1\">\n <svg className=\"size-5 text-text-link\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-2 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4\"\n />\n </svg>\n </div>\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold text-text-primary\">Enterprise</h3>\n <span className=\"text-xs font-medium px-2 py-0.5 rounded-full bg-bg-surface text-text-link shrink-0\">\n Contact Us\n </span>\n </div>\n <p className=\"text-sm text-text-secondary\">Dedicated infra · SSO/SCIM · Custom SLA</p>\n </div>\n\n <div className=\"text-right\">\n <div className=\"font-semibold text-text-primary\">Custom pricing</div>\n <div className=\"text-sm text-text-secondary\">contract-based</div>\n </div>\n\n <a\n href=\"mailto:contact@burdenoff.com?subject=Enterprise Plan Inquiry\"\n className=\"px-3 py-1.5 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors duration-200 shrink-0\"\n >\n Contact Us\n </a>\n </div>\n);\n\n// ============================================================================\n// Browse Plan Card (no edit/toggle)\n// ============================================================================\n\nconst BrowsePlanCard: FC<{\n plan: Plan;\n highlighted?: boolean;\n onView: () => void;\n onSubscribe: () => void;\n}> = ({ plan, highlighted = false, onView, onSubscribe }) => {\n const featuredQuotas = getFeaturedQuotas(plan.features || []);\n const allQuotas = combineQuotas(plan.features || []);\n const extraCount = allQuotas.length - featuredQuotas.length;\n const displayCurrency = useDefaultBillingCurrency();\n const { price, currency } = getDisplayPrice(\n plan.price,\n plan.currency,\n plan.currencyPrices as Record<string, number> | null,\n displayCurrency\n );\n\n return (\n <div\n className={`relative flex flex-col h-full rounded-card bg-bg-surface p-6 transition-[border-color,box-shadow] duration-200 ${\n highlighted\n ? 'border-2 border-action-primary-bg shadow-[var(--shadow-glow)]'\n : 'border border-border-subtle shadow-elevation-1 hover:border-border-strong hover:shadow-elevation-2'\n }`}\n >\n {highlighted && (\n <span className=\"absolute -top-3 left-1/2 -translate-x-1/2 inline-flex items-center gap-1 rounded-full bg-action-primary-bg px-3 py-1 text-xs font-semibold text-action-primary-text shadow-elevation-2\">\n <Sparkles className=\"size-3.5\" aria-hidden=\"true\" />\n Most popular\n </span>\n )}\n\n <div className=\"mb-4\">\n <h3 className=\"text-lg font-semibold text-text-primary\">{plan.name}</h3>\n <span className=\"text-sm text-text-secondary\">\n {formatPlanDurationLabel(plan.duration)}\n </span>\n </div>\n\n <div className=\"flex items-baseline gap-1.5 mb-5\">\n <span className=\"text-4xl font-bold tabular-nums tracking-tight text-text-primary\">\n {formatCurrency(price, currency)}\n </span>\n <span className=\"text-text-secondary text-sm\">/ {formatPlanDuration(plan.duration)}</span>\n </div>\n\n {featuredQuotas.length > 0 && (\n <ul className=\"space-y-2.5 mb-5 flex-1\">\n {featuredQuotas.map((quota) => (\n <li key={quota.quotaId} className=\"flex items-center justify-between gap-2 text-sm\">\n <span className=\"flex items-center gap-2 min-w-0\">\n <span className=\"flex size-5 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)]\">\n <Check className=\"size-3 text-action-primary-bg\" aria-hidden=\"true\" />\n </span>\n <span className=\"truncate text-text-primary\">\n {FEATURED_QUOTA_LABELS[quota.name] ?? quota.name}\n </span>\n </span>\n <span className=\"font-semibold text-text-primary tabular-nums shrink-0\">\n {formatFeaturedQuotaValue(quota.name, quota.totalValue)}\n </span>\n </li>\n ))}\n {extraCount > 0 && (\n <li className=\"text-sm text-text-secondary pl-7\">+{extraCount} more — see details</li>\n )}\n </ul>\n )}\n\n <div className=\"space-y-2 pt-4 border-t border-border-subtle mt-auto\">\n <button\n type=\"button\"\n onClick={onSubscribe}\n className=\"group inline-flex w-full items-center justify-center gap-2 rounded-button bg-action-primary-bg px-4 py-2.5 text-sm font-semibold text-action-primary-text shadow-elevation-1 transition-colors duration-200 hover:bg-action-primary-bgHover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n Subscribe\n <ArrowRight className=\"size-4 transition-transform duration-200\" aria-hidden=\"true\" />\n </button>\n <button\n type=\"button\"\n onClick={onView}\n className=\"w-full px-4 py-2 text-sm font-medium border border-border-subtle text-text-secondary rounded-button hover:bg-bg-sunken hover:text-text-primary hover:border-border-strong transition-colors duration-200\"\n >\n View details\n </button>\n </div>\n </div>\n );\n};\n\n// ============================================================================\n// Browse Plan Row (no edit/toggle)\n// ============================================================================\n\nconst BrowsePlanRow: FC<{ plan: Plan; onView: () => void; onSubscribe: () => void }> = ({\n plan,\n onView,\n onSubscribe,\n}) => {\n const featuredQuotas = getFeaturedQuotas(plan.features || []);\n const displayCurrency = useDefaultBillingCurrency();\n\n return (\n <div className=\"flex items-center gap-4 p-5 border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 transition-all duration-200 hover:border-border-strong hover:shadow-elevation-2\">\n <div className=\"size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center shrink-0\">\n <svg\n className=\"size-5 text-text-link\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10\"\n />\n </svg>\n </div>\n\n <div className=\"flex-1 min-w-0\">\n <h3 className=\"font-semibold text-text-primary truncate\">{plan.name}</h3>\n <p className=\"text-sm text-text-secondary\">\n {featuredQuotas.length} key feature{featuredQuotas.length !== 1 ? 's' : ''} included\n </p>\n </div>\n\n <div className=\"text-right\">\n <div className=\"font-semibold text-text-primary\">\n {(() => {\n const { price, currency } = getDisplayPrice(\n plan.price,\n plan.currency,\n plan.currencyPrices as Record<string, number> | null,\n displayCurrency\n );\n return formatCurrency(price, currency);\n })()}\n </div>\n <div className=\"text-sm text-text-secondary\">per {formatPlanDuration(plan.duration)}</div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={onSubscribe}\n className=\"px-3 py-1.5 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors duration-200\"\n >\n Subscribe\n </button>\n <button\n type=\"button\"\n onClick={onView}\n className=\"px-3 py-1.5 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken hover:border-border-strong transition-all duration-200\"\n >\n View\n </button>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PlansBrowsePage.js","names":[],"sources":["../../../../../src/billing/modules/plans/pages/PlansBrowsePage.tsx"],"sourcesContent":["/**\n * Plans Module - Plans Browse Page (User-facing)\n * Browse available plans and subscribe. No create/edit/toggle actions.\n */\n\nimport { useState, type FC } from 'react';\nimport { Check, ArrowRight, Sparkles } from 'lucide-react';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { usePlans } from '../hooks';\nimport { ServerError } from '../../../shared/components';\nimport {\n formatCurrency,\n formatPlanDuration,\n formatPlanDurationLabel,\n getPlanFeatureQuantity,\n isServerError,\n} from '../../../shared/utils';\nimport { PlanDuration } from '../../../shared/types';\nimport type { Plan, PlanFeature } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { PagePurpose, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport {\n useDefaultBillingCurrency,\n getDisplayPrice,\n} from '../../../hooks/useDefaultBillingCurrency';\nimport {\n FEATURED_QUOTA_NAMES,\n FEATURED_QUOTA_LABELS,\n formatFeaturedQuotaValue,\n} from '../constants/featuredQuotas';\nimport {\n enterpriseMailto,\n getEnterpriseOffer,\n type EnterpriseOffer,\n} from '../constants/enterprise';\n\ntype ViewMode = 'grid' | 'list';\n\ninterface CombinedQuota {\n quotaId: string;\n name: string;\n totalValue: number;\n}\n\nfunction combineQuotas(features: PlanFeature[]): CombinedQuota[] {\n const quotaMap = new Map<string, CombinedQuota>();\n for (const feature of features) {\n if (!feature.quota) continue;\n const quotaId = feature.quota.id;\n const limitValue = getPlanFeatureQuantity(feature);\n const existing = quotaMap.get(quotaId);\n if (existing) {\n existing.totalValue += limitValue;\n } else {\n quotaMap.set(quotaId, {\n quotaId,\n name: feature.quota.name ?? 'Unknown Quota',\n totalValue: limitValue,\n });\n }\n }\n return Array.from(quotaMap.values());\n}\n\n/** Returns only the featured quotas, in the canonical display order. */\nfunction getFeaturedQuotas(features: PlanFeature[]): CombinedQuota[] {\n const all = combineQuotas(features);\n const byName = new Map(all.map((q) => [q.name, q]));\n return FEATURED_QUOTA_NAMES.flatMap((name) => {\n const q = byName.get(name);\n return q ? [q] : [];\n });\n}\n\nexport const PlansBrowsePage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const navigateTo = useBillingNavigate();\n const { productId, plansDocsUrl } = useBilling();\n const enterpriseOffer = getEnterpriseOffer(productId);\n const { plans, isLoading, error, refetch } = usePlans({\n includeFree: false,\n productId: productId ?? undefined,\n });\n\n const [viewMode, setViewMode] = useState<ViewMode>('grid');\n const [billingInterval, setBillingInterval] = useState<PlanDuration>(PlanDuration.MONTHLY);\n\n // Only show active, publicly purchasable plans to users\n const filteredPlans = plans.filter(\n (plan) => plan.isActive && plan.isPurchasable !== false && plan.duration === billingInterval\n );\n\n // Spotlight a recommended tier (the second plan when there are 2+, the common\n // \"mid / Pro\" sweet-spot) so the eye lands on a clear primary choice.\n const recommendedIndex = filteredPlans.length >= 2 ? 1 : -1;\n\n if (isLoading && plans.length === 0) {\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n <div className=\"h-8 w-48 bg-bg-sunken animate-pulse rounded-card\" />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-6\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 p-5 space-y-4\"\n >\n <div className=\"h-6 w-24 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-8 w-32 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"space-y-2\">\n {[1, 2, 3].map((j) => (\n <div key={j} className=\"h-4 w-full bg-bg-sunken animate-pulse rounded\" />\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n if (error && isServerError(error)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title={tr('billing.plans.serverUnavailable', 'Server Unavailable')}\n message={tr('billing.plans.unableToLoadPlans', 'Unable to load plans.')}\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n {/* Header */}\n <div>\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n {tr('billing.plans.browsePlansTitle', 'Plans')}\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr('billing.plans.browsePlansSubtitle', 'Choose a plan that fits your needs')}\n </p>\n <PagePurpose\n className=\"mt-3\"\n learnMoreHref={plansDocsUrl}\n learnMoreLabel={tr('billing.plans.learnMore', 'Learn more')}\n >\n {tr(\n 'billing.plans.browsePurpose',\n 'Compare what each plan includes — quotas, features and price — and subscribe to the one that fits your team. Switch between monthly and yearly billing to see the difference, or contact us for a custom Enterprise contract.'\n )}\n </PagePurpose>\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-wrap items-center gap-4 pb-4 border-b border-border-subtle\">\n <div className=\"inline-flex rounded-lg border border-border-subtle p-1 bg-bg-sunken\">\n {([PlanDuration.MONTHLY, PlanDuration.YEARLY] as const).map((interval) => (\n <button\n type=\"button\"\n key={interval}\n onClick={() => setBillingInterval(interval)}\n className={`px-3 py-1.5 text-sm font-medium rounded-button transition-colors ${\n billingInterval === interval\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n >\n {interval === PlanDuration.MONTHLY ? 'Monthly' : 'Yearly'}\n </button>\n ))}\n </div>\n\n <div className=\"ml-auto inline-flex rounded-lg border border-border-subtle p-1 bg-bg-sunken\">\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`p-1.5 rounded-button transition-colors ${\n viewMode === 'grid'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n title=\"Grid view\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`p-1.5 rounded-button transition-colors ${\n viewMode === 'list'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n title=\"List view\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 6h16M4 12h16M4 18h16\"\n />\n </svg>\n </button>\n </div>\n </div>\n\n {/* Plans */}\n {filteredPlans.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={tr('billing.plans.noPlansAvailable', 'No plans available')}\n description={tr('billing.plans.checkBackLater', 'Check back later for new plans.')}\n />\n ) : viewMode === 'grid' ? (\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 items-stretch\">\n {filteredPlans.map((plan, idx) => (\n <BrowsePlanCard\n key={plan.id}\n plan={plan}\n highlighted={idx === recommendedIndex}\n onView={() => navigateTo(`/plans/${plan.id}`)}\n onSubscribe={() => navigateTo(`/checkout/${plan.id}`)}\n />\n ))}\n <EnterpriseCard offer={enterpriseOffer} />\n </div>\n ) : (\n <div className=\"space-y-3\">\n {filteredPlans.map((plan) => (\n <BrowsePlanRow\n key={plan.id}\n plan={plan}\n onView={() => navigateTo(`/plans/${plan.id}`)}\n onSubscribe={() => navigateTo(`/checkout/${plan.id}`)}\n />\n ))}\n <EnterpriseRow offer={enterpriseOffer} />\n </div>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Static Enterprise \"Contact Us\" card — no backend plan record\n// ============================================================================\n\nconst EnterpriseCard: FC<{ offer: EnterpriseOffer }> = ({ offer }) => (\n <div className=\"border border-border-strong bg-[var(--color-accent-soft)] rounded-card shadow-elevation-1 p-5 flex flex-col h-full\">\n <div className=\"mb-4\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Enterprise</h3>\n <span className=\"text-xs font-medium px-2 py-0.5 rounded-full bg-bg-surface text-text-link\">\n Contact Us\n </span>\n </div>\n <span className=\"text-sm text-text-secondary\">Custom contract</span>\n </div>\n\n <div className=\"flex items-baseline gap-1 mb-4\">\n <span className=\"text-2xl font-semibold text-text-primary\">Custom pricing</span>\n </div>\n\n <ul className=\"space-y-2 mb-4 flex-1\">\n {offer.highlights.map((item) => (\n <li key={item} className=\"flex items-center gap-2 text-sm\">\n <svg\n className=\"size-4 text-status-success-text shrink-0\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M5 13l4 4L19 7\" />\n </svg>\n <span className=\"text-text-primary\">{item}</span>\n </li>\n ))}\n </ul>\n\n <div className=\"space-y-2 pt-4 border-t border-border-subtle mt-auto\">\n <a\n href={enterpriseMailto(offer)}\n className=\"block w-full px-4 py-2 text-sm font-medium text-center bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors duration-200\"\n >\n Contact Us\n </a>\n </div>\n </div>\n);\n\nconst EnterpriseRow: FC<{ offer: EnterpriseOffer }> = ({ offer }) => (\n <div className=\"flex items-center gap-4 p-5 border border-border-strong rounded-card bg-[var(--color-accent-soft)] shadow-elevation-1 transition-all duration-200 hover:shadow-elevation-2\">\n <div className=\"size-10 rounded-lg bg-bg-surface flex items-center justify-center shrink-0 shadow-elevation-1\">\n <svg className=\"size-5 text-text-link\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-2 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4\"\n />\n </svg>\n </div>\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold text-text-primary\">Enterprise</h3>\n <span className=\"text-xs font-medium px-2 py-0.5 rounded-full bg-bg-surface text-text-link shrink-0\">\n Contact Us\n </span>\n </div>\n <p className=\"text-sm text-text-secondary\">{offer.summary}</p>\n </div>\n\n <div className=\"text-right\">\n <div className=\"font-semibold text-text-primary\">Custom pricing</div>\n <div className=\"text-sm text-text-secondary\">contract-based</div>\n </div>\n\n <a\n href={enterpriseMailto(offer)}\n className=\"px-3 py-1.5 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors duration-200 shrink-0\"\n >\n Contact Us\n </a>\n </div>\n);\n\n// ============================================================================\n// Browse Plan Card (no edit/toggle)\n// ============================================================================\n\nconst BrowsePlanCard: FC<{\n plan: Plan;\n highlighted?: boolean;\n onView: () => void;\n onSubscribe: () => void;\n}> = ({ plan, highlighted = false, onView, onSubscribe }) => {\n const featuredQuotas = getFeaturedQuotas(plan.features || []);\n const allQuotas = combineQuotas(plan.features || []);\n const extraCount = allQuotas.length - featuredQuotas.length;\n const displayCurrency = useDefaultBillingCurrency();\n const { price, currency } = getDisplayPrice(\n plan.price,\n plan.currency,\n plan.currencyPrices as Record<string, number> | null,\n displayCurrency\n );\n\n return (\n <div\n className={`relative flex flex-col h-full rounded-card bg-bg-surface p-6 transition-[border-color,box-shadow] duration-200 ${\n highlighted\n ? 'border-2 border-action-primary-bg shadow-[var(--shadow-glow)]'\n : 'border border-border-subtle shadow-elevation-1 hover:border-border-strong hover:shadow-elevation-2'\n }`}\n >\n {highlighted && (\n <span className=\"absolute -top-3 left-1/2 -translate-x-1/2 inline-flex items-center gap-1 rounded-full bg-action-primary-bg px-3 py-1 text-xs font-semibold text-action-primary-text shadow-elevation-2\">\n <Sparkles className=\"size-3.5\" aria-hidden=\"true\" />\n Most popular\n </span>\n )}\n\n <div className=\"mb-4\">\n <h3 className=\"text-lg font-semibold text-text-primary\">{plan.name}</h3>\n <span className=\"text-sm text-text-secondary\">\n {formatPlanDurationLabel(plan.duration)}\n </span>\n </div>\n\n <div className=\"flex items-baseline gap-1.5 mb-5\">\n <span className=\"text-4xl font-bold tabular-nums tracking-tight text-text-primary\">\n {formatCurrency(price, currency)}\n </span>\n <span className=\"text-text-secondary text-sm\">/ {formatPlanDuration(plan.duration)}</span>\n </div>\n\n {featuredQuotas.length > 0 && (\n <ul className=\"space-y-2.5 mb-5 flex-1\">\n {featuredQuotas.map((quota) => (\n <li key={quota.quotaId} className=\"flex items-center justify-between gap-2 text-sm\">\n <span className=\"flex items-center gap-2 min-w-0\">\n <span className=\"flex size-5 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)]\">\n <Check className=\"size-3 text-action-primary-bg\" aria-hidden=\"true\" />\n </span>\n <span className=\"truncate text-text-primary\">\n {FEATURED_QUOTA_LABELS[quota.name] ?? quota.name}\n </span>\n </span>\n <span className=\"font-semibold text-text-primary tabular-nums shrink-0\">\n {formatFeaturedQuotaValue(quota.name, quota.totalValue)}\n </span>\n </li>\n ))}\n {extraCount > 0 && (\n <li className=\"text-sm text-text-secondary pl-7\">+{extraCount} more — see details</li>\n )}\n </ul>\n )}\n\n <div className=\"space-y-2 pt-4 border-t border-border-subtle mt-auto\">\n <button\n type=\"button\"\n onClick={onSubscribe}\n className=\"group inline-flex w-full items-center justify-center gap-2 rounded-button bg-action-primary-bg px-4 py-2.5 text-sm font-semibold text-action-primary-text shadow-elevation-1 transition-colors duration-200 hover:bg-action-primary-bgHover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n Subscribe\n <ArrowRight className=\"size-4 transition-transform duration-200\" aria-hidden=\"true\" />\n </button>\n <button\n type=\"button\"\n onClick={onView}\n className=\"w-full px-4 py-2 text-sm font-medium border border-border-subtle text-text-secondary rounded-button hover:bg-bg-sunken hover:text-text-primary hover:border-border-strong transition-colors duration-200\"\n >\n View details\n </button>\n </div>\n </div>\n );\n};\n\n// ============================================================================\n// Browse Plan Row (no edit/toggle)\n// ============================================================================\n\nconst BrowsePlanRow: FC<{ plan: Plan; onView: () => void; onSubscribe: () => void }> = ({\n plan,\n onView,\n onSubscribe,\n}) => {\n const featuredQuotas = getFeaturedQuotas(plan.features || []);\n const displayCurrency = useDefaultBillingCurrency();\n\n return (\n <div className=\"flex items-center gap-4 p-5 border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 transition-all duration-200 hover:border-border-strong hover:shadow-elevation-2\">\n <div className=\"size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center shrink-0\">\n <svg\n className=\"size-5 text-text-link\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10\"\n />\n </svg>\n </div>\n\n <div className=\"flex-1 min-w-0\">\n <h3 className=\"font-semibold text-text-primary truncate\">{plan.name}</h3>\n <p className=\"text-sm text-text-secondary\">\n {featuredQuotas.length} key feature{featuredQuotas.length !== 1 ? 's' : ''} included\n </p>\n </div>\n\n <div className=\"text-right\">\n <div className=\"font-semibold text-text-primary\">\n {(() => {\n const { price, currency } = getDisplayPrice(\n plan.price,\n plan.currency,\n plan.currencyPrices as Record<string, number> | null,\n displayCurrency\n );\n return formatCurrency(price, currency);\n })()}\n </div>\n <div className=\"text-sm text-text-secondary\">per {formatPlanDuration(plan.duration)}</div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={onSubscribe}\n className=\"px-3 py-1.5 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors duration-200\"\n >\n Subscribe\n </button>\n <button\n type=\"button\"\n onClick={onView}\n className=\"px-3 py-1.5 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken hover:border-border-strong transition-all duration-200\"\n >\n View\n </button>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -7,32 +7,33 @@ var a = e(null), o = () => {
|
|
|
7
7
|
let e = t(a);
|
|
8
8
|
if (!e) throw Error("useBilling must be used within BillingProvider");
|
|
9
9
|
return e;
|
|
10
|
-
}, s = ({ children: e, basePath: t, navigate: o, currentUser: s, workspaceId: c, orgId: l, productId: u,
|
|
11
|
-
let
|
|
10
|
+
}, s = ({ children: e, basePath: t, navigate: o, currentUser: s, workspaceId: c, orgId: l, productId: u, plansDocsUrl: d, apiGatewayUrl: f, authToken: p, stripePublishableKey: m, razorpayKey: h, onSubscriptionCreate: g, onSubscriptionUpdate: _, onPaymentProcess: v, registerNavItems: y, registerFooterItems: b }) => {
|
|
11
|
+
let x = {
|
|
12
12
|
basePath: t,
|
|
13
13
|
navigate: o,
|
|
14
14
|
currentUser: s,
|
|
15
15
|
workspaceId: c,
|
|
16
16
|
orgId: l,
|
|
17
17
|
productId: u,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
plansDocsUrl: d,
|
|
19
|
+
apiGatewayUrl: f,
|
|
20
|
+
authToken: p,
|
|
21
|
+
stripePublishableKey: m,
|
|
22
|
+
razorpayKey: h,
|
|
23
|
+
onSubscriptionCreate: g,
|
|
24
|
+
onSubscriptionUpdate: _,
|
|
25
|
+
onPaymentProcess: v,
|
|
26
|
+
registerNavItems: y,
|
|
27
|
+
registerFooterItems: b
|
|
28
|
+
}, S = /* @__PURE__ */ i(a.Provider, {
|
|
29
|
+
value: x,
|
|
29
30
|
children: /* @__PURE__ */ i(r, {
|
|
30
31
|
scopeId: l,
|
|
31
32
|
gateway: "global",
|
|
32
33
|
children: e
|
|
33
34
|
})
|
|
34
35
|
});
|
|
35
|
-
return t ?
|
|
36
|
+
return t ? S : /* @__PURE__ */ i(n, { children: S });
|
|
36
37
|
};
|
|
37
38
|
//#endregion
|
|
38
39
|
export { s as BillingProvider, o as useBilling };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BillingProvider.js","names":[],"sources":["../../../src/billing/providers/BillingProvider.tsx"],"sourcesContent":["import type { FC, PropsWithChildren } from 'react';\nimport { createContext, useContext } from 'react';\nimport { MemoryRouter } from 'react-router';\nimport { PermissionProvider } from '@burdenoff/fe-libs/shared/providers/shell';\nimport type { BillingRootProps } from '../types';\n\n/**\n * Context value for Billing microfrontend\n */\ntype BillingContextValue = BillingRootProps;\n\nconst BillingContext = createContext<BillingContextValue | null>(null);\n\n/**\n * Hook to access Billing context\n */\nexport const useBilling = () => {\n const context = useContext(BillingContext);\n if (!context) {\n throw new Error('useBilling must be used within BillingProvider');\n }\n return context;\n};\n\n/**\n * Provider component for Billing microfrontend\n * Sets up routing and context\n *\n * When integrated into a shell (basePath provided), the shell already has a router\n * so we don't create another one. When standalone (no basePath), use MemoryRouter.\n */\nexport const BillingProvider: FC<PropsWithChildren<BillingRootProps>> = ({\n children,\n basePath,\n navigate,\n currentUser,\n workspaceId,\n orgId,\n productId,\n apiGatewayUrl,\n authToken,\n stripePublishableKey,\n razorpayKey,\n onSubscriptionCreate,\n onSubscriptionUpdate,\n onPaymentProcess,\n registerNavItems,\n registerFooterItems,\n}) => {\n const contextValue: BillingContextValue = {\n basePath,\n navigate,\n currentUser,\n workspaceId,\n orgId,\n productId,\n apiGatewayUrl,\n authToken,\n stripePublishableKey,\n razorpayKey,\n onSubscriptionCreate,\n onSubscriptionUpdate,\n onPaymentProcess,\n registerNavItems,\n registerFooterItems,\n };\n\n const content = (\n <BillingContext.Provider value={contextValue}>\n <PermissionProvider scopeId={orgId} gateway=\"global\">\n {children}\n </PermissionProvider>\n </BillingContext.Provider>\n );\n\n // When integrated into a shell (basePath provided), the shell already has a router\n // so we don't create another one. When standalone (no basePath), use MemoryRouter.\n if (basePath) {\n return content;\n }\n\n // Standalone mode: create MemoryRouter for independent operation\n return <MemoryRouter>{content}</MemoryRouter>;\n};\n"],"mappings":";;;;;AAWA,IAAM,IAAiB,EAA0C,IAAI,GAKxD,UAAmB;CAC9B,IAAM,IAAU,EAAW,CAAc;CACzC,IAAI,CAAC,GACH,MAAU,MAAM,gDAAgD;CAElE,OAAO;AACT,GASa,KAA4D,EACvE,aACA,aACA,aACA,gBACA,gBACA,UACA,cACA,kBACA,cACA,yBACA,gBACA,yBACA,yBACA,qBACA,qBACA,6BACI;CACJ,IAAM,IAAoC;EACxC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,GAEM,IACJ,kBAAC,EAAe,UAAhB;EAAyB,OAAO;YAC9B,kBAAC,GAAD;GAAoB,SAAS;GAAO,SAAQ;GACzC;EACiB,CAAA;CACG,CAAA;CAU3B,OALI,IACK,IAIF,kBAAC,GAAD,EAAA,UAAe,EAAsB,CAAA;AAC9C"}
|
|
1
|
+
{"version":3,"file":"BillingProvider.js","names":[],"sources":["../../../src/billing/providers/BillingProvider.tsx"],"sourcesContent":["import type { FC, PropsWithChildren } from 'react';\nimport { createContext, useContext } from 'react';\nimport { MemoryRouter } from 'react-router';\nimport { PermissionProvider } from '@burdenoff/fe-libs/shared/providers/shell';\nimport type { BillingRootProps } from '../types';\n\n/**\n * Context value for Billing microfrontend\n */\ntype BillingContextValue = BillingRootProps;\n\nconst BillingContext = createContext<BillingContextValue | null>(null);\n\n/**\n * Hook to access Billing context\n */\nexport const useBilling = () => {\n const context = useContext(BillingContext);\n if (!context) {\n throw new Error('useBilling must be used within BillingProvider');\n }\n return context;\n};\n\n/**\n * Provider component for Billing microfrontend\n * Sets up routing and context\n *\n * When integrated into a shell (basePath provided), the shell already has a router\n * so we don't create another one. When standalone (no basePath), use MemoryRouter.\n */\nexport const BillingProvider: FC<PropsWithChildren<BillingRootProps>> = ({\n children,\n basePath,\n navigate,\n currentUser,\n workspaceId,\n orgId,\n productId,\n plansDocsUrl,\n apiGatewayUrl,\n authToken,\n stripePublishableKey,\n razorpayKey,\n onSubscriptionCreate,\n onSubscriptionUpdate,\n onPaymentProcess,\n registerNavItems,\n registerFooterItems,\n}) => {\n const contextValue: BillingContextValue = {\n basePath,\n navigate,\n currentUser,\n workspaceId,\n orgId,\n productId,\n plansDocsUrl,\n apiGatewayUrl,\n authToken,\n stripePublishableKey,\n razorpayKey,\n onSubscriptionCreate,\n onSubscriptionUpdate,\n onPaymentProcess,\n registerNavItems,\n registerFooterItems,\n };\n\n const content = (\n <BillingContext.Provider value={contextValue}>\n <PermissionProvider scopeId={orgId} gateway=\"global\">\n {children}\n </PermissionProvider>\n </BillingContext.Provider>\n );\n\n // When integrated into a shell (basePath provided), the shell already has a router\n // so we don't create another one. When standalone (no basePath), use MemoryRouter.\n if (basePath) {\n return content;\n }\n\n // Standalone mode: create MemoryRouter for independent operation\n return <MemoryRouter>{content}</MemoryRouter>;\n};\n"],"mappings":";;;;;AAWA,IAAM,IAAiB,EAA0C,IAAI,GAKxD,UAAmB;CAC9B,IAAM,IAAU,EAAW,CAAc;CACzC,IAAI,CAAC,GACH,MAAU,MAAM,gDAAgD;CAElE,OAAO;AACT,GASa,KAA4D,EACvE,aACA,aACA,aACA,gBACA,gBACA,UACA,cACA,iBACA,kBACA,cACA,yBACA,gBACA,yBACA,yBACA,qBACA,qBACA,6BACI;CACJ,IAAM,IAAoC;EACxC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,GAEM,IACJ,kBAAC,EAAe,UAAhB;EAAyB,OAAO;YAC9B,kBAAC,GAAD;GAAoB,SAAS;GAAO,SAAQ;GACzC;EACiB,CAAA;CACG,CAAA;CAU3B,OALI,IACK,IAIF,kBAAC,GAAD,EAAA,UAAe,EAAsB,CAAA;AAC9C"}
|