@monetizekit/react 0.1.0
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 +63 -0
- package/dist/index.cjs +571 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +234 -0
- package/dist/index.d.ts +234 -0
- package/dist/index.js +552 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
import { createContext, useMemo, useContext, useState, useEffect } from 'react';
|
|
2
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
// src/provider.tsx
|
|
5
|
+
|
|
6
|
+
// src/lib/client.ts
|
|
7
|
+
var MonetizeKitClient = class {
|
|
8
|
+
constructor(config) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
}
|
|
11
|
+
async get(path) {
|
|
12
|
+
const headers = {
|
|
13
|
+
Authorization: `Bearer ${this.config.publishableKey}`
|
|
14
|
+
};
|
|
15
|
+
if (this.config.customerToken) {
|
|
16
|
+
headers["X-Customer-Token"] = this.config.customerToken;
|
|
17
|
+
}
|
|
18
|
+
const res = await fetch(`${this.config.baseUrl}${path}`, { headers });
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
throw new Error(`MonetizeKit API ${res.status} for ${path}`);
|
|
21
|
+
}
|
|
22
|
+
return await res.json();
|
|
23
|
+
}
|
|
24
|
+
listPlans() {
|
|
25
|
+
return this.get("/api/v1/plans?page=1&pageSize=100");
|
|
26
|
+
}
|
|
27
|
+
getEntitlement(customerId, featureKey) {
|
|
28
|
+
return this.get(`/api/v1/entitlements/${customerId}/${encodeURIComponent(featureKey)}`);
|
|
29
|
+
}
|
|
30
|
+
getUsage(customerId, meterId) {
|
|
31
|
+
return this.get(`/api/v1/usage/${customerId}/${encodeURIComponent(meterId)}`);
|
|
32
|
+
}
|
|
33
|
+
getCredits(customerId) {
|
|
34
|
+
return this.get(`/api/v1/credits/${customerId}`);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// src/theme/tokens.ts
|
|
39
|
+
var THEME_PRESETS = {
|
|
40
|
+
light: {
|
|
41
|
+
colorBackground: "#ffffff",
|
|
42
|
+
colorForeground: "#0a0a0a",
|
|
43
|
+
colorMuted: "#71717a",
|
|
44
|
+
colorPrimary: "#4f46e5",
|
|
45
|
+
colorPrimaryForeground: "#ffffff",
|
|
46
|
+
colorAccent: "#10b981",
|
|
47
|
+
colorBorder: "#e4e4e7",
|
|
48
|
+
radius: "0.5rem",
|
|
49
|
+
fontFamily: "system-ui, -apple-system, sans-serif"
|
|
50
|
+
},
|
|
51
|
+
dark: {
|
|
52
|
+
colorBackground: "#0a0a0a",
|
|
53
|
+
colorForeground: "#fafafa",
|
|
54
|
+
colorMuted: "#a1a1aa",
|
|
55
|
+
colorPrimary: "#6366f1",
|
|
56
|
+
colorPrimaryForeground: "#0a0a0a",
|
|
57
|
+
colorAccent: "#10b981",
|
|
58
|
+
colorBorder: "#27272a",
|
|
59
|
+
radius: "0.5rem",
|
|
60
|
+
fontFamily: "system-ui, -apple-system, sans-serif"
|
|
61
|
+
},
|
|
62
|
+
memphis: {
|
|
63
|
+
colorBackground: "#FFFEF2",
|
|
64
|
+
colorForeground: "#1a1a1a",
|
|
65
|
+
colorMuted: "#5b5b52",
|
|
66
|
+
colorPrimary: "#FF6B35",
|
|
67
|
+
colorPrimaryForeground: "#1a1a1a",
|
|
68
|
+
colorAccent: "#00D9FF",
|
|
69
|
+
colorBorder: "#1a1a1a",
|
|
70
|
+
radius: "0",
|
|
71
|
+
fontFamily: "system-ui, -apple-system, sans-serif"
|
|
72
|
+
},
|
|
73
|
+
dashboard: {
|
|
74
|
+
colorBackground: "oklch(1 0 0)",
|
|
75
|
+
colorForeground: "oklch(0.145 0 0)",
|
|
76
|
+
colorMuted: "oklch(0.556 0 0)",
|
|
77
|
+
colorPrimary: "oklch(0.205 0 0)",
|
|
78
|
+
colorPrimaryForeground: "oklch(0.985 0 0)",
|
|
79
|
+
colorAccent: "oklch(0.97 0 0)",
|
|
80
|
+
colorBorder: "oklch(0.922 0 0)",
|
|
81
|
+
radius: "0.625rem",
|
|
82
|
+
fontFamily: "Geist, system-ui, sans-serif"
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
function resolveTokens(appearance = "light") {
|
|
86
|
+
if (typeof appearance === "string") {
|
|
87
|
+
return THEME_PRESETS[appearance];
|
|
88
|
+
}
|
|
89
|
+
const base = THEME_PRESETS[appearance.preset ?? "light"];
|
|
90
|
+
return { ...base, ...appearance.tokens };
|
|
91
|
+
}
|
|
92
|
+
var TOKEN_TO_CSS_VAR = {
|
|
93
|
+
colorBackground: "--mk-bg",
|
|
94
|
+
colorForeground: "--mk-fg",
|
|
95
|
+
colorMuted: "--mk-muted",
|
|
96
|
+
colorPrimary: "--mk-primary",
|
|
97
|
+
colorPrimaryForeground: "--mk-primary-fg",
|
|
98
|
+
colorAccent: "--mk-accent",
|
|
99
|
+
colorBorder: "--mk-border",
|
|
100
|
+
radius: "--mk-radius",
|
|
101
|
+
fontFamily: "--mk-font"
|
|
102
|
+
};
|
|
103
|
+
function tokensToStyle(tokens) {
|
|
104
|
+
const style = {};
|
|
105
|
+
Object.keys(tokens).forEach((key) => {
|
|
106
|
+
style[TOKEN_TO_CSS_VAR[key]] = String(tokens[key]);
|
|
107
|
+
});
|
|
108
|
+
return style;
|
|
109
|
+
}
|
|
110
|
+
var MonetizeKitContext = createContext(null);
|
|
111
|
+
function MonetizeKitProvider({
|
|
112
|
+
publishableKey,
|
|
113
|
+
baseUrl,
|
|
114
|
+
customerToken,
|
|
115
|
+
customerId,
|
|
116
|
+
appearance = "light",
|
|
117
|
+
children
|
|
118
|
+
}) {
|
|
119
|
+
const value = useMemo(() => {
|
|
120
|
+
return {
|
|
121
|
+
client: new MonetizeKitClient({ publishableKey, baseUrl, customerToken }),
|
|
122
|
+
customerId,
|
|
123
|
+
appearance,
|
|
124
|
+
tokens: resolveTokens(appearance)
|
|
125
|
+
};
|
|
126
|
+
}, [publishableKey, baseUrl, customerToken, customerId, appearance]);
|
|
127
|
+
return /* @__PURE__ */ jsx(MonetizeKitContext.Provider, { value, children });
|
|
128
|
+
}
|
|
129
|
+
function useMonetizeKit() {
|
|
130
|
+
const ctx = useContext(MonetizeKitContext);
|
|
131
|
+
if (!ctx) {
|
|
132
|
+
throw new Error("useMonetizeKit must be used within a <MonetizeKitProvider>.");
|
|
133
|
+
}
|
|
134
|
+
return ctx;
|
|
135
|
+
}
|
|
136
|
+
function useAsync(run, deps) {
|
|
137
|
+
const [state, setState] = useState({
|
|
138
|
+
data: null,
|
|
139
|
+
loading: true,
|
|
140
|
+
error: null
|
|
141
|
+
});
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
let active = true;
|
|
144
|
+
const promise = run();
|
|
145
|
+
if (!promise) {
|
|
146
|
+
setState({ data: null, loading: false, error: null });
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
150
|
+
promise.then((data) => {
|
|
151
|
+
if (active) setState({ data, loading: false, error: null });
|
|
152
|
+
}).catch((error) => {
|
|
153
|
+
if (active) {
|
|
154
|
+
setState({
|
|
155
|
+
data: null,
|
|
156
|
+
loading: false,
|
|
157
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
return () => {
|
|
162
|
+
active = false;
|
|
163
|
+
};
|
|
164
|
+
}, deps);
|
|
165
|
+
return state;
|
|
166
|
+
}
|
|
167
|
+
function useEntitlement(featureKey) {
|
|
168
|
+
const { client, customerId } = useMonetizeKit();
|
|
169
|
+
const state = useAsync(
|
|
170
|
+
() => customerId ? client.getEntitlement(customerId, featureKey) : null,
|
|
171
|
+
[client, customerId, featureKey]
|
|
172
|
+
);
|
|
173
|
+
return {
|
|
174
|
+
value: state.data?.value ?? null,
|
|
175
|
+
allowed: state.data?.allowed ?? false,
|
|
176
|
+
loading: state.loading,
|
|
177
|
+
error: state.error
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function useUsage(meterId) {
|
|
181
|
+
const { client, customerId } = useMonetizeKit();
|
|
182
|
+
const state = useAsync(
|
|
183
|
+
() => customerId ? client.getUsage(customerId, meterId) : null,
|
|
184
|
+
[client, customerId, meterId]
|
|
185
|
+
);
|
|
186
|
+
return {
|
|
187
|
+
current: state.data?.current ?? 0,
|
|
188
|
+
limit: state.data?.limit ?? null,
|
|
189
|
+
loading: state.loading,
|
|
190
|
+
error: state.error
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function useCredits() {
|
|
194
|
+
const { client, customerId } = useMonetizeKit();
|
|
195
|
+
const state = useAsync(
|
|
196
|
+
() => customerId ? client.getCredits(customerId) : null,
|
|
197
|
+
[client, customerId]
|
|
198
|
+
);
|
|
199
|
+
return {
|
|
200
|
+
balance: state.data?.balance ?? 0,
|
|
201
|
+
currency: state.data?.currency,
|
|
202
|
+
loading: state.loading,
|
|
203
|
+
error: state.error
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// src/lib/format.ts
|
|
208
|
+
function formatMoney(amount, currency = "USD", locale) {
|
|
209
|
+
return new Intl.NumberFormat(locale, {
|
|
210
|
+
style: "currency",
|
|
211
|
+
currency,
|
|
212
|
+
minimumFractionDigits: Number.isInteger(amount) ? 0 : 2,
|
|
213
|
+
maximumFractionDigits: amount < 1 ? 3 : 2
|
|
214
|
+
}).format(amount);
|
|
215
|
+
}
|
|
216
|
+
function formatUnits(value, locale) {
|
|
217
|
+
return new Intl.NumberFormat(locale, {
|
|
218
|
+
notation: value >= 1e4 ? "compact" : "standard",
|
|
219
|
+
maximumFractionDigits: 1
|
|
220
|
+
}).format(value);
|
|
221
|
+
}
|
|
222
|
+
var INTERVAL_SUFFIX = {
|
|
223
|
+
monthly: "/mo",
|
|
224
|
+
annually: "/yr",
|
|
225
|
+
one_time: ""
|
|
226
|
+
};
|
|
227
|
+
function describePlanPrice(plan, locale) {
|
|
228
|
+
const pricing = plan.pricing ?? [];
|
|
229
|
+
const contactSales = (plan.tags ?? []).includes("contact_sales") || pricing.length === 0;
|
|
230
|
+
if (contactSales) {
|
|
231
|
+
return { headline: null, contactSales: true };
|
|
232
|
+
}
|
|
233
|
+
const base = pricing.find((t) => t.type === "flat");
|
|
234
|
+
const hasUsage = pricing.some((t) => t.type === "usage");
|
|
235
|
+
const currency = base?.currency ?? pricing[0]?.currency ?? "USD";
|
|
236
|
+
const interval = base?.interval ?? pricing[0]?.interval ?? "monthly";
|
|
237
|
+
const baseAmount = base?.amount ?? 0;
|
|
238
|
+
const headline = `${formatMoney(baseAmount, currency, locale)}${INTERVAL_SUFFIX[interval]}`;
|
|
239
|
+
return {
|
|
240
|
+
headline,
|
|
241
|
+
caption: hasUsage ? "+ usage" : void 0,
|
|
242
|
+
contactSales: false
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
function describeUsageTerm(term, locale) {
|
|
246
|
+
const parts = [];
|
|
247
|
+
if (term.includedUnits && term.includedUnits > 0) {
|
|
248
|
+
parts.push(`${formatUnits(term.includedUnits, locale)} ${term.meterDisplayName ?? "units"} included`);
|
|
249
|
+
}
|
|
250
|
+
const tiers = term.tieredPricing ?? [];
|
|
251
|
+
if (tiers.length > 0) {
|
|
252
|
+
const first = tiers[0];
|
|
253
|
+
parts.push(
|
|
254
|
+
`then ${formatMoney(first.unitPrice, term.currency, locale)}/unit${term.tierMode === "volume" ? " (volume)" : ""}`
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
return parts.join(", ");
|
|
258
|
+
}
|
|
259
|
+
var wrapperStyle = {
|
|
260
|
+
display: "grid",
|
|
261
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(240px, 1fr))",
|
|
262
|
+
gap: "1.5rem",
|
|
263
|
+
background: "var(--mk-bg)",
|
|
264
|
+
color: "var(--mk-fg)",
|
|
265
|
+
fontFamily: "var(--mk-font)"
|
|
266
|
+
};
|
|
267
|
+
var cardBase = {
|
|
268
|
+
border: "1px solid var(--mk-border)",
|
|
269
|
+
borderRadius: "var(--mk-radius)",
|
|
270
|
+
padding: "1.5rem",
|
|
271
|
+
display: "flex",
|
|
272
|
+
flexDirection: "column",
|
|
273
|
+
gap: "0.75rem"
|
|
274
|
+
};
|
|
275
|
+
function PricingTable({
|
|
276
|
+
plans: plansProp,
|
|
277
|
+
highlightPlan,
|
|
278
|
+
locale,
|
|
279
|
+
onSelectPlan,
|
|
280
|
+
onContactSales
|
|
281
|
+
}) {
|
|
282
|
+
const { client, tokens } = useMonetizeKit();
|
|
283
|
+
const [plans, setPlans] = useState(plansProp ?? null);
|
|
284
|
+
const [error, setError] = useState(null);
|
|
285
|
+
useEffect(() => {
|
|
286
|
+
if (plansProp) {
|
|
287
|
+
setPlans(plansProp);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
let active = true;
|
|
291
|
+
client.listPlans().then((res) => {
|
|
292
|
+
if (active) setPlans(res.data ?? []);
|
|
293
|
+
}).catch((e) => {
|
|
294
|
+
if (active) setError(e instanceof Error ? e : new Error(String(e)));
|
|
295
|
+
});
|
|
296
|
+
return () => {
|
|
297
|
+
active = false;
|
|
298
|
+
};
|
|
299
|
+
}, [client, plansProp]);
|
|
300
|
+
if (error) {
|
|
301
|
+
return /* @__PURE__ */ jsx("div", { role: "alert", style: { color: "var(--mk-muted)" }, children: "Unable to load pricing." });
|
|
302
|
+
}
|
|
303
|
+
if (!plans) {
|
|
304
|
+
return /* @__PURE__ */ jsx("div", { "aria-busy": "true", style: { color: "var(--mk-muted)" }, children: "Loading pricing\u2026" });
|
|
305
|
+
}
|
|
306
|
+
return /* @__PURE__ */ jsx("div", { style: { ...tokensToStyle(tokens), ...wrapperStyle }, "data-mk-component": "pricing-table", children: plans.map((plan) => {
|
|
307
|
+
const price = describePlanPrice(plan, locale);
|
|
308
|
+
const highlighted = highlightPlan != null && plan.name.toLowerCase() === highlightPlan.toLowerCase();
|
|
309
|
+
return /* @__PURE__ */ jsxs(
|
|
310
|
+
"div",
|
|
311
|
+
{
|
|
312
|
+
style: {
|
|
313
|
+
...cardBase,
|
|
314
|
+
borderColor: highlighted ? "var(--mk-primary)" : "var(--mk-border)",
|
|
315
|
+
borderWidth: highlighted ? 2 : 1
|
|
316
|
+
},
|
|
317
|
+
"data-mk-plan": plan.name,
|
|
318
|
+
"data-mk-highlighted": highlighted ? "true" : void 0,
|
|
319
|
+
children: [
|
|
320
|
+
highlighted ? /* @__PURE__ */ jsx(
|
|
321
|
+
"span",
|
|
322
|
+
{
|
|
323
|
+
style: {
|
|
324
|
+
alignSelf: "flex-start",
|
|
325
|
+
background: "var(--mk-primary)",
|
|
326
|
+
color: "var(--mk-primary-fg)",
|
|
327
|
+
borderRadius: "var(--mk-radius)",
|
|
328
|
+
padding: "0.125rem 0.5rem",
|
|
329
|
+
fontSize: "0.75rem",
|
|
330
|
+
fontWeight: 600
|
|
331
|
+
},
|
|
332
|
+
children: "Most Popular"
|
|
333
|
+
}
|
|
334
|
+
) : null,
|
|
335
|
+
/* @__PURE__ */ jsx("h3", { style: { margin: 0, fontSize: "1.25rem", fontWeight: 700 }, children: plan.name }),
|
|
336
|
+
plan.description ? /* @__PURE__ */ jsx("p", { style: { margin: 0, color: "var(--mk-muted)", fontSize: "0.875rem" }, children: plan.description }) : null,
|
|
337
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "baseline", gap: "0.375rem" }, children: [
|
|
338
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "2rem", fontWeight: 700 }, children: price.contactSales ? "Custom" : price.headline }),
|
|
339
|
+
price.caption ? /* @__PURE__ */ jsx("span", { style: { color: "var(--mk-muted)", fontSize: "0.875rem" }, children: price.caption }) : null
|
|
340
|
+
] }),
|
|
341
|
+
plan.entitlements && plan.entitlements.length > 0 ? /* @__PURE__ */ jsx("ul", { style: { margin: 0, paddingLeft: "1rem", color: "var(--mk-fg)", fontSize: "0.875rem" }, children: plan.entitlements.slice(0, 6).map((e) => /* @__PURE__ */ jsxs("li", { children: [
|
|
342
|
+
e.featureDisplayName,
|
|
343
|
+
e.type !== "boolean" ? `: ${String(e.value)}` : ""
|
|
344
|
+
] }, e.featureKey)) }) : null,
|
|
345
|
+
/* @__PURE__ */ jsx(
|
|
346
|
+
"button",
|
|
347
|
+
{
|
|
348
|
+
type: "button",
|
|
349
|
+
onClick: () => price.contactSales ? onContactSales?.(plan.id) : onSelectPlan?.(plan.id),
|
|
350
|
+
style: {
|
|
351
|
+
marginTop: "auto",
|
|
352
|
+
background: "var(--mk-primary)",
|
|
353
|
+
color: "var(--mk-primary-fg)",
|
|
354
|
+
border: "none",
|
|
355
|
+
borderRadius: "var(--mk-radius)",
|
|
356
|
+
padding: "0.625rem 1rem",
|
|
357
|
+
fontWeight: 600,
|
|
358
|
+
cursor: "pointer"
|
|
359
|
+
},
|
|
360
|
+
children: price.contactSales ? "Contact Sales" : "Get started"
|
|
361
|
+
}
|
|
362
|
+
)
|
|
363
|
+
]
|
|
364
|
+
},
|
|
365
|
+
plan.id
|
|
366
|
+
);
|
|
367
|
+
}) });
|
|
368
|
+
}
|
|
369
|
+
var overlayStyle = {
|
|
370
|
+
border: "1px solid var(--mk-border)",
|
|
371
|
+
borderRadius: "var(--mk-radius)",
|
|
372
|
+
padding: "2rem",
|
|
373
|
+
textAlign: "center",
|
|
374
|
+
background: "var(--mk-bg)",
|
|
375
|
+
color: "var(--mk-fg)",
|
|
376
|
+
fontFamily: "var(--mk-font)",
|
|
377
|
+
display: "flex",
|
|
378
|
+
flexDirection: "column",
|
|
379
|
+
gap: "0.75rem",
|
|
380
|
+
alignItems: "center"
|
|
381
|
+
};
|
|
382
|
+
function Paywall({
|
|
383
|
+
feature,
|
|
384
|
+
children,
|
|
385
|
+
title = "Upgrade to unlock this feature",
|
|
386
|
+
description = "This feature isn't included in your current plan.",
|
|
387
|
+
ctaLabel = "Upgrade",
|
|
388
|
+
onUpgrade
|
|
389
|
+
}) {
|
|
390
|
+
const { allowed, loading } = useEntitlement(feature);
|
|
391
|
+
const { tokens } = useMonetizeKit();
|
|
392
|
+
if (loading) {
|
|
393
|
+
return /* @__PURE__ */ jsx("div", { "aria-busy": "true", style: { color: "var(--mk-muted)" }, children: "Checking access\u2026" });
|
|
394
|
+
}
|
|
395
|
+
if (allowed) {
|
|
396
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
397
|
+
}
|
|
398
|
+
return /* @__PURE__ */ jsxs("div", { style: { ...tokensToStyle(tokens), ...overlayStyle }, "data-mk-component": "paywall", children: [
|
|
399
|
+
/* @__PURE__ */ jsx("h3", { style: { margin: 0, fontSize: "1.125rem", fontWeight: 700 }, children: title }),
|
|
400
|
+
/* @__PURE__ */ jsx("p", { style: { margin: 0, color: "var(--mk-muted)", fontSize: "0.875rem" }, children: description }),
|
|
401
|
+
/* @__PURE__ */ jsx(
|
|
402
|
+
"button",
|
|
403
|
+
{
|
|
404
|
+
type: "button",
|
|
405
|
+
onClick: onUpgrade,
|
|
406
|
+
style: {
|
|
407
|
+
background: "var(--mk-primary)",
|
|
408
|
+
color: "var(--mk-primary-fg)",
|
|
409
|
+
border: "none",
|
|
410
|
+
borderRadius: "var(--mk-radius)",
|
|
411
|
+
padding: "0.625rem 1.25rem",
|
|
412
|
+
fontWeight: 600,
|
|
413
|
+
cursor: "pointer"
|
|
414
|
+
},
|
|
415
|
+
children: ctaLabel
|
|
416
|
+
}
|
|
417
|
+
)
|
|
418
|
+
] });
|
|
419
|
+
}
|
|
420
|
+
var bannerStyle = {
|
|
421
|
+
border: "1px solid var(--mk-border)",
|
|
422
|
+
borderRadius: "var(--mk-radius)",
|
|
423
|
+
padding: "0.875rem 1rem",
|
|
424
|
+
background: "var(--mk-bg)",
|
|
425
|
+
color: "var(--mk-fg)",
|
|
426
|
+
fontFamily: "var(--mk-font)",
|
|
427
|
+
display: "flex",
|
|
428
|
+
flexDirection: "column",
|
|
429
|
+
gap: "0.5rem"
|
|
430
|
+
};
|
|
431
|
+
function UsageBanner({
|
|
432
|
+
meterId,
|
|
433
|
+
label = "Usage",
|
|
434
|
+
locale,
|
|
435
|
+
warnAt = 0.8
|
|
436
|
+
}) {
|
|
437
|
+
const { current, limit, loading } = useUsage(meterId);
|
|
438
|
+
const { tokens } = useMonetizeKit();
|
|
439
|
+
if (loading) {
|
|
440
|
+
return /* @__PURE__ */ jsx("div", { "aria-busy": "true", style: { color: "var(--mk-muted)" }, children: "Loading usage\u2026" });
|
|
441
|
+
}
|
|
442
|
+
const hasLimit = typeof limit === "number" && limit > 0;
|
|
443
|
+
const fraction = hasLimit ? Math.min(1, current / limit) : 0;
|
|
444
|
+
const over = hasLimit && current > limit;
|
|
445
|
+
const warn = hasLimit && fraction >= warnAt;
|
|
446
|
+
const barColor = over || warn ? "var(--mk-primary)" : "var(--mk-accent)";
|
|
447
|
+
return /* @__PURE__ */ jsxs("div", { style: { ...tokensToStyle(tokens), ...bannerStyle }, "data-mk-component": "usage-banner", children: [
|
|
448
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", fontSize: "0.875rem" }, children: [
|
|
449
|
+
/* @__PURE__ */ jsx("span", { style: { fontWeight: 600 }, children: label }),
|
|
450
|
+
/* @__PURE__ */ jsxs("span", { style: { color: "var(--mk-muted)" }, children: [
|
|
451
|
+
formatUnits(current, locale),
|
|
452
|
+
hasLimit ? ` / ${formatUnits(limit, locale)}` : ""
|
|
453
|
+
] })
|
|
454
|
+
] }),
|
|
455
|
+
hasLimit ? /* @__PURE__ */ jsx(
|
|
456
|
+
"div",
|
|
457
|
+
{
|
|
458
|
+
style: {
|
|
459
|
+
height: 6,
|
|
460
|
+
borderRadius: 999,
|
|
461
|
+
background: "var(--mk-border)",
|
|
462
|
+
overflow: "hidden"
|
|
463
|
+
},
|
|
464
|
+
role: "progressbar",
|
|
465
|
+
"aria-valuenow": Math.round(fraction * 100),
|
|
466
|
+
"aria-valuemin": 0,
|
|
467
|
+
"aria-valuemax": 100,
|
|
468
|
+
children: /* @__PURE__ */ jsx("div", { style: { width: `${fraction * 100}%`, height: "100%", background: barColor } })
|
|
469
|
+
}
|
|
470
|
+
) : null,
|
|
471
|
+
over ? /* @__PURE__ */ jsx("span", { style: { color: "var(--mk-primary)", fontSize: "0.75rem" }, children: "Over included allotment \u2014 overage billed per usage pricing." }) : null
|
|
472
|
+
] });
|
|
473
|
+
}
|
|
474
|
+
var containerStyle = {
|
|
475
|
+
border: "1px solid var(--mk-border)",
|
|
476
|
+
borderRadius: "var(--mk-radius)",
|
|
477
|
+
padding: "1.25rem",
|
|
478
|
+
background: "var(--mk-bg)",
|
|
479
|
+
color: "var(--mk-fg)",
|
|
480
|
+
fontFamily: "var(--mk-font)",
|
|
481
|
+
display: "flex",
|
|
482
|
+
flexDirection: "column",
|
|
483
|
+
gap: "1rem",
|
|
484
|
+
maxWidth: 480
|
|
485
|
+
};
|
|
486
|
+
function CustomerPortal({
|
|
487
|
+
planName = "Current plan",
|
|
488
|
+
meterIds = [],
|
|
489
|
+
showCredits = true,
|
|
490
|
+
locale,
|
|
491
|
+
currency = "USD",
|
|
492
|
+
onManageBilling
|
|
493
|
+
}) {
|
|
494
|
+
const { tokens } = useMonetizeKit();
|
|
495
|
+
const credits = useCredits();
|
|
496
|
+
return /* @__PURE__ */ jsxs("div", { style: { ...tokensToStyle(tokens), ...containerStyle }, "data-mk-component": "customer-portal", children: [
|
|
497
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
|
|
498
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
499
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.75rem", color: "var(--mk-muted)" }, children: "Plan" }),
|
|
500
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "1.125rem", fontWeight: 700 }, children: planName })
|
|
501
|
+
] }),
|
|
502
|
+
/* @__PURE__ */ jsx(
|
|
503
|
+
"button",
|
|
504
|
+
{
|
|
505
|
+
type: "button",
|
|
506
|
+
onClick: onManageBilling,
|
|
507
|
+
style: {
|
|
508
|
+
background: "var(--mk-primary)",
|
|
509
|
+
color: "var(--mk-primary-fg)",
|
|
510
|
+
border: "none",
|
|
511
|
+
borderRadius: "var(--mk-radius)",
|
|
512
|
+
padding: "0.5rem 0.875rem",
|
|
513
|
+
fontWeight: 600,
|
|
514
|
+
cursor: "pointer"
|
|
515
|
+
},
|
|
516
|
+
children: "Manage billing"
|
|
517
|
+
}
|
|
518
|
+
)
|
|
519
|
+
] }),
|
|
520
|
+
meterIds.map((meterId) => /* @__PURE__ */ jsx(UsageBanner, { meterId, label: meterId, locale }, meterId)),
|
|
521
|
+
showCredits ? /* @__PURE__ */ jsxs(
|
|
522
|
+
"div",
|
|
523
|
+
{
|
|
524
|
+
style: {
|
|
525
|
+
border: "1px solid var(--mk-border)",
|
|
526
|
+
borderRadius: "var(--mk-radius)",
|
|
527
|
+
padding: "0.875rem 1rem",
|
|
528
|
+
display: "flex",
|
|
529
|
+
justifyContent: "space-between"
|
|
530
|
+
},
|
|
531
|
+
children: [
|
|
532
|
+
/* @__PURE__ */ jsx("span", { style: { fontWeight: 600, fontSize: "0.875rem" }, children: "Credits" }),
|
|
533
|
+
/* @__PURE__ */ jsx("span", { style: { color: "var(--mk-muted)" }, children: credits.loading ? "\u2026" : formatMoney(credits.balance, credits.currency ?? currency, locale) })
|
|
534
|
+
]
|
|
535
|
+
}
|
|
536
|
+
) : null
|
|
537
|
+
] });
|
|
538
|
+
}
|
|
539
|
+
function EntitlementGate({
|
|
540
|
+
feature,
|
|
541
|
+
children,
|
|
542
|
+
fallback = null,
|
|
543
|
+
loadingFallback = null
|
|
544
|
+
}) {
|
|
545
|
+
const { allowed, loading } = useEntitlement(feature);
|
|
546
|
+
if (loading) return /* @__PURE__ */ jsx(Fragment, { children: loadingFallback });
|
|
547
|
+
return /* @__PURE__ */ jsx(Fragment, { children: allowed ? children : fallback });
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
export { CustomerPortal, EntitlementGate, MonetizeKitClient, MonetizeKitProvider, Paywall, PricingTable, THEME_PRESETS, UsageBanner, describePlanPrice, describeUsageTerm, formatMoney, formatUnits, resolveTokens, tokensToStyle, useCredits, useEntitlement, useMonetizeKit, useUsage };
|
|
551
|
+
//# sourceMappingURL=index.js.map
|
|
552
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/client.ts","../src/theme/tokens.ts","../src/provider.tsx","../src/hooks.ts","../src/lib/format.ts","../src/components/PricingTable.tsx","../src/components/Paywall.tsx","../src/components/UsageBanner.tsx","../src/components/CustomerPortal.tsx","../src/components/EntitlementGate.tsx"],"names":["useState","useEffect","jsx","jsxs","Fragment"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Minimal fetch client for the MonetizeKit public API. Uses a publishable key\n * (pk_*) as a Bearer token; safe for browser use against allowlisted origins.\n */\nexport interface MonetizeKitClientConfig {\n publishableKey: string;\n baseUrl: string;\n customerToken?: string;\n}\n\nexport class MonetizeKitClient {\n constructor(private readonly config: MonetizeKitClientConfig) {}\n\n private async get<T>(path: string): Promise<T> {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.config.publishableKey}`,\n };\n if (this.config.customerToken) {\n headers[\"X-Customer-Token\"] = this.config.customerToken;\n }\n const res = await fetch(`${this.config.baseUrl}${path}`, { headers });\n if (!res.ok) {\n throw new Error(`MonetizeKit API ${res.status} for ${path}`);\n }\n return (await res.json()) as T;\n }\n\n listPlans<T>(): Promise<T> {\n return this.get<T>(\"/api/v1/plans?page=1&pageSize=100\");\n }\n\n getEntitlement<T>(customerId: string, featureKey: string): Promise<T> {\n return this.get<T>(`/api/v1/entitlements/${customerId}/${encodeURIComponent(featureKey)}`);\n }\n\n getUsage<T>(customerId: string, meterId: string): Promise<T> {\n return this.get<T>(`/api/v1/usage/${customerId}/${encodeURIComponent(meterId)}`);\n }\n\n getCredits<T>(customerId: string): Promise<T> {\n return this.get<T>(`/api/v1/credits/${customerId}`);\n }\n}\n","/**\n * Appearance/theme token contract for MonetizeKit components. Components consume\n * CSS custom properties derived from these tokens, so a host app can pass a\n * preset name, a partial token override, or both.\n *\n * Presets:\n * - `memphis` — the marketing site identity (coral/yellow/cyan, sharp corners)\n * - `dashboard` — the app's neutral palette (rounded, muted)\n * - `light` / `dark` — neutral defaults\n */\nimport type { CSSProperties } from \"react\";\n\nexport interface ThemeTokens {\n colorBackground: string;\n colorForeground: string;\n colorMuted: string;\n colorPrimary: string;\n colorPrimaryForeground: string;\n colorAccent: string;\n colorBorder: string;\n radius: string;\n fontFamily: string;\n}\n\nexport type ThemePresetName = \"light\" | \"dark\" | \"memphis\" | \"dashboard\";\n\nexport const THEME_PRESETS: Record<ThemePresetName, ThemeTokens> = {\n light: {\n colorBackground: \"#ffffff\",\n colorForeground: \"#0a0a0a\",\n colorMuted: \"#71717a\",\n colorPrimary: \"#4f46e5\",\n colorPrimaryForeground: \"#ffffff\",\n colorAccent: \"#10b981\",\n colorBorder: \"#e4e4e7\",\n radius: \"0.5rem\",\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n },\n dark: {\n colorBackground: \"#0a0a0a\",\n colorForeground: \"#fafafa\",\n colorMuted: \"#a1a1aa\",\n colorPrimary: \"#6366f1\",\n colorPrimaryForeground: \"#0a0a0a\",\n colorAccent: \"#10b981\",\n colorBorder: \"#27272a\",\n radius: \"0.5rem\",\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n },\n memphis: {\n colorBackground: \"#FFFEF2\",\n colorForeground: \"#1a1a1a\",\n colorMuted: \"#5b5b52\",\n colorPrimary: \"#FF6B35\",\n colorPrimaryForeground: \"#1a1a1a\",\n colorAccent: \"#00D9FF\",\n colorBorder: \"#1a1a1a\",\n radius: \"0\",\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n },\n dashboard: {\n colorBackground: \"oklch(1 0 0)\",\n colorForeground: \"oklch(0.145 0 0)\",\n colorMuted: \"oklch(0.556 0 0)\",\n colorPrimary: \"oklch(0.205 0 0)\",\n colorPrimaryForeground: \"oklch(0.985 0 0)\",\n colorAccent: \"oklch(0.97 0 0)\",\n colorBorder: \"oklch(0.922 0 0)\",\n radius: \"0.625rem\",\n fontFamily: \"Geist, system-ui, sans-serif\",\n },\n};\n\nexport type Appearance =\n | ThemePresetName\n | { preset?: ThemePresetName; tokens?: Partial<ThemeTokens> };\n\n/** Resolve an appearance prop into a concrete token set. */\nexport function resolveTokens(appearance: Appearance = \"light\"): ThemeTokens {\n if (typeof appearance === \"string\") {\n return THEME_PRESETS[appearance];\n }\n const base = THEME_PRESETS[appearance.preset ?? \"light\"];\n return { ...base, ...appearance.tokens };\n}\n\nconst TOKEN_TO_CSS_VAR: Record<keyof ThemeTokens, string> = {\n colorBackground: \"--mk-bg\",\n colorForeground: \"--mk-fg\",\n colorMuted: \"--mk-muted\",\n colorPrimary: \"--mk-primary\",\n colorPrimaryForeground: \"--mk-primary-fg\",\n colorAccent: \"--mk-accent\",\n colorBorder: \"--mk-border\",\n radius: \"--mk-radius\",\n fontFamily: \"--mk-font\",\n};\n\n/** Convert tokens into a CSS custom-property style object for a wrapper element. */\nexport function tokensToStyle(tokens: ThemeTokens): CSSProperties {\n const style: Record<string, string> = {};\n (Object.keys(tokens) as (keyof ThemeTokens)[]).forEach((key) => {\n style[TOKEN_TO_CSS_VAR[key]] = String(tokens[key]);\n });\n return style as CSSProperties;\n}\n\n","import {\n createContext,\n useContext,\n useMemo,\n type ReactNode,\n} from \"react\";\nimport { MonetizeKitClient } from \"./lib/client\";\nimport {\n resolveTokens,\n type Appearance,\n type ThemeTokens,\n} from \"./theme/tokens\";\n\nexport interface MonetizeKitContextValue {\n client: MonetizeKitClient;\n customerId?: string;\n appearance: Appearance;\n tokens: ThemeTokens;\n}\n\nconst MonetizeKitContext = createContext<MonetizeKitContextValue | null>(null);\n\nexport interface MonetizeKitProviderProps {\n /** Browser-safe publishable key (pk_*). */\n publishableKey: string;\n /** API origin, e.g. https://app.monetizekit.app. */\n baseUrl: string;\n /** Optional customer JWT for customer-scoped reads (entitlements/usage/credits). */\n customerToken?: string;\n /** Customer id for entitlement/usage/credit hooks. */\n customerId?: string;\n /** Theme preset name or token overrides. */\n appearance?: Appearance;\n children: ReactNode;\n}\n\nexport function MonetizeKitProvider({\n publishableKey,\n baseUrl,\n customerToken,\n customerId,\n appearance = \"light\",\n children,\n}: MonetizeKitProviderProps) {\n const value = useMemo<MonetizeKitContextValue>(() => {\n return {\n client: new MonetizeKitClient({ publishableKey, baseUrl, customerToken }),\n customerId,\n appearance,\n tokens: resolveTokens(appearance),\n };\n }, [publishableKey, baseUrl, customerToken, customerId, appearance]);\n\n return (\n <MonetizeKitContext.Provider value={value}>\n {children}\n </MonetizeKitContext.Provider>\n );\n}\n\nexport function useMonetizeKit(): MonetizeKitContextValue {\n const ctx = useContext(MonetizeKitContext);\n if (!ctx) {\n throw new Error(\"useMonetizeKit must be used within a <MonetizeKitProvider>.\");\n }\n return ctx;\n}\n","import { useEffect, useState } from \"react\";\nimport { useMonetizeKit } from \"./provider\";\nimport type { CreditBalance, EntitlementResult, UsageResult } from \"./types\";\n\ninterface AsyncState<T> {\n data: T | null;\n loading: boolean;\n error: Error | null;\n}\n\nfunction useAsync<T>(\n run: () => Promise<T> | null,\n deps: unknown[],\n): AsyncState<T> {\n const [state, setState] = useState<AsyncState<T>>({\n data: null,\n loading: true,\n error: null,\n });\n\n useEffect(() => {\n let active = true;\n const promise = run();\n if (!promise) {\n setState({ data: null, loading: false, error: null });\n return;\n }\n setState((prev) => ({ ...prev, loading: true, error: null }));\n promise\n .then((data) => {\n if (active) setState({ data, loading: false, error: null });\n })\n .catch((error: unknown) => {\n if (active) {\n setState({\n data: null,\n loading: false,\n error: error instanceof Error ? error : new Error(String(error)),\n });\n }\n });\n return () => {\n active = false;\n };\n }, deps);\n\n return state;\n}\n\n/** Resolve a single feature entitlement for the provider's customer. */\nexport function useEntitlement(featureKey: string) {\n const { client, customerId } = useMonetizeKit();\n const state = useAsync<EntitlementResult>(\n () =>\n customerId\n ? client.getEntitlement<EntitlementResult>(customerId, featureKey)\n : null,\n [client, customerId, featureKey],\n );\n return {\n value: state.data?.value ?? null,\n allowed: state.data?.allowed ?? false,\n loading: state.loading,\n error: state.error,\n };\n}\n\n/** Resolve current usage for a meter. */\nexport function useUsage(meterId: string) {\n const { client, customerId } = useMonetizeKit();\n const state = useAsync<UsageResult>(\n () => (customerId ? client.getUsage<UsageResult>(customerId, meterId) : null),\n [client, customerId, meterId],\n );\n return {\n current: state.data?.current ?? 0,\n limit: state.data?.limit ?? null,\n loading: state.loading,\n error: state.error,\n };\n}\n\n/** Resolve the customer's credit balance. */\nexport function useCredits() {\n const { client, customerId } = useMonetizeKit();\n const state = useAsync<CreditBalance>(\n () => (customerId ? client.getCredits<CreditBalance>(customerId) : null),\n [client, customerId],\n );\n return {\n balance: state.data?.balance ?? 0,\n currency: state.data?.currency,\n loading: state.loading,\n error: state.error,\n };\n}\n","import type { Plan, PricingTerm } from \"../types\";\n\n/** Format a monetary amount with Intl, honoring locale + currency. */\nexport function formatMoney(\n amount: number,\n currency = \"USD\",\n locale?: string,\n): string {\n return new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency,\n minimumFractionDigits: Number.isInteger(amount) ? 0 : 2,\n maximumFractionDigits: amount < 1 ? 3 : 2,\n }).format(amount);\n}\n\n/** Format a large unit count compactly (e.g. 5,000 / 1M). */\nexport function formatUnits(value: number, locale?: string): string {\n return new Intl.NumberFormat(locale, {\n notation: value >= 10_000 ? \"compact\" : \"standard\",\n maximumFractionDigits: 1,\n }).format(value);\n}\n\nexport interface PriceDisplay {\n /** Headline price string, or null for contact-sales. */\n headline: string | null;\n /** Secondary line (e.g. \"+ usage\" or billing interval). */\n caption?: string;\n contactSales: boolean;\n}\n\nconst INTERVAL_SUFFIX: Record<PricingTerm[\"interval\"], string> = {\n monthly: \"/mo\",\n annually: \"/yr\",\n one_time: \"\",\n};\n\n/**\n * Derive a display from a plan's pricing terms. Returns a contact-sales display\n * when the plan is tagged `contact_sales` or has no public price; renders a\n * \"from $base + usage\" headline when metered usage terms are present.\n */\nexport function describePlanPrice(\n plan: Plan,\n locale?: string,\n): PriceDisplay {\n const pricing = plan.pricing ?? [];\n const contactSales =\n (plan.tags ?? []).includes(\"contact_sales\") || pricing.length === 0;\n if (contactSales) {\n return { headline: null, contactSales: true };\n }\n\n const base = pricing.find((t) => t.type === \"flat\");\n const hasUsage = pricing.some((t) => t.type === \"usage\");\n const currency = base?.currency ?? pricing[0]?.currency ?? \"USD\";\n const interval = base?.interval ?? pricing[0]?.interval ?? \"monthly\";\n const baseAmount = base?.amount ?? 0;\n\n const headline = `${formatMoney(baseAmount, currency, locale)}${INTERVAL_SUFFIX[interval]}`;\n return {\n headline,\n caption: hasUsage ? \"+ usage\" : undefined,\n contactSales: false,\n };\n}\n\n/** Human-readable description of a usage term's metered tiers. */\nexport function describeUsageTerm(term: PricingTerm, locale?: string): string {\n const parts: string[] = [];\n if (term.includedUnits && term.includedUnits > 0) {\n parts.push(`${formatUnits(term.includedUnits, locale)} ${term.meterDisplayName ?? \"units\"} included`);\n }\n const tiers = term.tieredPricing ?? [];\n if (tiers.length > 0) {\n const first = tiers[0]!;\n parts.push(\n `then ${formatMoney(first.unitPrice, term.currency, locale)}/unit${\n term.tierMode === \"volume\" ? \" (volume)\" : \"\"\n }`,\n );\n }\n return parts.join(\", \");\n}\n","import { useEffect, useState, type CSSProperties } from \"react\";\nimport { useMonetizeKit } from \"../provider\";\nimport { tokensToStyle } from \"../theme/tokens\";\nimport { describePlanPrice } from \"../lib/format\";\nimport type { Plan } from \"../types\";\n\nexport interface PricingTableProps {\n /** Plans to render; if omitted, fetched live from the publishable-key API. */\n plans?: Plan[];\n /** Plan name to highlight as \"Most Popular\". */\n highlightPlan?: string;\n billingCycle?: \"monthly\" | \"annually\";\n locale?: string;\n onSelectPlan?: (planId: string) => void;\n /** Where the Contact Sales CTA links (defaults to no-op). */\n onContactSales?: (planId: string) => void;\n}\n\nconst wrapperStyle: CSSProperties = {\n display: \"grid\",\n gridTemplateColumns: \"repeat(auto-fit, minmax(240px, 1fr))\",\n gap: \"1.5rem\",\n background: \"var(--mk-bg)\",\n color: \"var(--mk-fg)\",\n fontFamily: \"var(--mk-font)\",\n};\n\nconst cardBase: CSSProperties = {\n border: \"1px solid var(--mk-border)\",\n borderRadius: \"var(--mk-radius)\",\n padding: \"1.5rem\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"0.75rem\",\n};\n\nexport function PricingTable({\n plans: plansProp,\n highlightPlan,\n locale,\n onSelectPlan,\n onContactSales,\n}: PricingTableProps) {\n const { client, tokens } = useMonetizeKit();\n const [plans, setPlans] = useState<Plan[] | null>(plansProp ?? null);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (plansProp) {\n setPlans(plansProp);\n return;\n }\n let active = true;\n client\n .listPlans<{ data: Plan[] }>()\n .then((res) => {\n if (active) setPlans(res.data ?? []);\n })\n .catch((e: unknown) => {\n if (active) setError(e instanceof Error ? e : new Error(String(e)));\n });\n return () => {\n active = false;\n };\n }, [client, plansProp]);\n\n if (error) {\n return <div role=\"alert\" style={{ color: \"var(--mk-muted)\" }}>Unable to load pricing.</div>;\n }\n if (!plans) {\n return <div aria-busy=\"true\" style={{ color: \"var(--mk-muted)\" }}>Loading pricing…</div>;\n }\n\n return (\n <div style={{ ...tokensToStyle(tokens), ...wrapperStyle }} data-mk-component=\"pricing-table\">\n {plans.map((plan) => {\n const price = describePlanPrice(plan, locale);\n const highlighted =\n highlightPlan != null &&\n plan.name.toLowerCase() === highlightPlan.toLowerCase();\n return (\n <div\n key={plan.id}\n style={{\n ...cardBase,\n borderColor: highlighted ? \"var(--mk-primary)\" : \"var(--mk-border)\",\n borderWidth: highlighted ? 2 : 1,\n }}\n data-mk-plan={plan.name}\n data-mk-highlighted={highlighted ? \"true\" : undefined}\n >\n {highlighted ? (\n <span\n style={{\n alignSelf: \"flex-start\",\n background: \"var(--mk-primary)\",\n color: \"var(--mk-primary-fg)\",\n borderRadius: \"var(--mk-radius)\",\n padding: \"0.125rem 0.5rem\",\n fontSize: \"0.75rem\",\n fontWeight: 600,\n }}\n >\n Most Popular\n </span>\n ) : null}\n <h3 style={{ margin: 0, fontSize: \"1.25rem\", fontWeight: 700 }}>{plan.name}</h3>\n {plan.description ? (\n <p style={{ margin: 0, color: \"var(--mk-muted)\", fontSize: \"0.875rem\" }}>\n {plan.description}\n </p>\n ) : null}\n <div style={{ display: \"flex\", alignItems: \"baseline\", gap: \"0.375rem\" }}>\n <span style={{ fontSize: \"2rem\", fontWeight: 700 }}>\n {price.contactSales ? \"Custom\" : price.headline}\n </span>\n {price.caption ? (\n <span style={{ color: \"var(--mk-muted)\", fontSize: \"0.875rem\" }}>\n {price.caption}\n </span>\n ) : null}\n </div>\n {plan.entitlements && plan.entitlements.length > 0 ? (\n <ul style={{ margin: 0, paddingLeft: \"1rem\", color: \"var(--mk-fg)\", fontSize: \"0.875rem\" }}>\n {plan.entitlements.slice(0, 6).map((e) => (\n <li key={e.featureKey}>\n {e.featureDisplayName}\n {e.type !== \"boolean\" ? `: ${String(e.value)}` : \"\"}\n </li>\n ))}\n </ul>\n ) : null}\n <button\n type=\"button\"\n onClick={() =>\n price.contactSales\n ? onContactSales?.(plan.id)\n : onSelectPlan?.(plan.id)\n }\n style={{\n marginTop: \"auto\",\n background: \"var(--mk-primary)\",\n color: \"var(--mk-primary-fg)\",\n border: \"none\",\n borderRadius: \"var(--mk-radius)\",\n padding: \"0.625rem 1rem\",\n fontWeight: 600,\n cursor: \"pointer\",\n }}\n >\n {price.contactSales ? \"Contact Sales\" : \"Get started\"}\n </button>\n </div>\n );\n })}\n </div>\n );\n}\n","import { type CSSProperties, type ReactNode } from \"react\";\nimport { useEntitlement } from \"../hooks\";\nimport { useMonetizeKit } from \"../provider\";\nimport { tokensToStyle } from \"../theme/tokens\";\n\nexport interface PaywallProps {\n /** Feature key gating the content. */\n feature: string;\n /** Protected content, shown when entitled. */\n children: ReactNode;\n title?: string;\n description?: string;\n ctaLabel?: string;\n onUpgrade?: () => void;\n}\n\nconst overlayStyle: CSSProperties = {\n border: \"1px solid var(--mk-border)\",\n borderRadius: \"var(--mk-radius)\",\n padding: \"2rem\",\n textAlign: \"center\",\n background: \"var(--mk-bg)\",\n color: \"var(--mk-fg)\",\n fontFamily: \"var(--mk-font)\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"0.75rem\",\n alignItems: \"center\",\n};\n\n/** Gate content behind an entitlement, showing an upgrade prompt when locked. */\nexport function Paywall({\n feature,\n children,\n title = \"Upgrade to unlock this feature\",\n description = \"This feature isn't included in your current plan.\",\n ctaLabel = \"Upgrade\",\n onUpgrade,\n}: PaywallProps) {\n const { allowed, loading } = useEntitlement(feature);\n const { tokens } = useMonetizeKit();\n\n if (loading) {\n return <div aria-busy=\"true\" style={{ color: \"var(--mk-muted)\" }}>Checking access…</div>;\n }\n if (allowed) {\n return <>{children}</>;\n }\n\n return (\n <div style={{ ...tokensToStyle(tokens), ...overlayStyle }} data-mk-component=\"paywall\">\n <h3 style={{ margin: 0, fontSize: \"1.125rem\", fontWeight: 700 }}>{title}</h3>\n <p style={{ margin: 0, color: \"var(--mk-muted)\", fontSize: \"0.875rem\" }}>{description}</p>\n <button\n type=\"button\"\n onClick={onUpgrade}\n style={{\n background: \"var(--mk-primary)\",\n color: \"var(--mk-primary-fg)\",\n border: \"none\",\n borderRadius: \"var(--mk-radius)\",\n padding: \"0.625rem 1.25rem\",\n fontWeight: 600,\n cursor: \"pointer\",\n }}\n >\n {ctaLabel}\n </button>\n </div>\n );\n}\n","import { type CSSProperties } from \"react\";\nimport { useUsage } from \"../hooks\";\nimport { useMonetizeKit } from \"../provider\";\nimport { tokensToStyle } from \"../theme/tokens\";\nimport { formatUnits } from \"../lib/format\";\n\nexport interface UsageBannerProps {\n /** Meter to display usage for. */\n meterId: string;\n label?: string;\n locale?: string;\n /** Fraction (0-1) at which the banner switches to a warning style. */\n warnAt?: number;\n}\n\nconst bannerStyle: CSSProperties = {\n border: \"1px solid var(--mk-border)\",\n borderRadius: \"var(--mk-radius)\",\n padding: \"0.875rem 1rem\",\n background: \"var(--mk-bg)\",\n color: \"var(--mk-fg)\",\n fontFamily: \"var(--mk-font)\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"0.5rem\",\n};\n\n/** Show current usage vs allotment for a capacity meter, with overage hint. */\nexport function UsageBanner({\n meterId,\n label = \"Usage\",\n locale,\n warnAt = 0.8,\n}: UsageBannerProps) {\n const { current, limit, loading } = useUsage(meterId);\n const { tokens } = useMonetizeKit();\n\n if (loading) {\n return <div aria-busy=\"true\" style={{ color: \"var(--mk-muted)\" }}>Loading usage…</div>;\n }\n\n const hasLimit = typeof limit === \"number\" && limit > 0;\n const fraction = hasLimit ? Math.min(1, current / (limit as number)) : 0;\n const over = hasLimit && current > (limit as number);\n const warn = hasLimit && fraction >= warnAt;\n const barColor = over || warn ? \"var(--mk-primary)\" : \"var(--mk-accent)\";\n\n return (\n <div style={{ ...tokensToStyle(tokens), ...bannerStyle }} data-mk-component=\"usage-banner\">\n <div style={{ display: \"flex\", justifyContent: \"space-between\", fontSize: \"0.875rem\" }}>\n <span style={{ fontWeight: 600 }}>{label}</span>\n <span style={{ color: \"var(--mk-muted)\" }}>\n {formatUnits(current, locale)}\n {hasLimit ? ` / ${formatUnits(limit as number, locale)}` : \"\"}\n </span>\n </div>\n {hasLimit ? (\n <div\n style={{\n height: 6,\n borderRadius: 999,\n background: \"var(--mk-border)\",\n overflow: \"hidden\",\n }}\n role=\"progressbar\"\n aria-valuenow={Math.round(fraction * 100)}\n aria-valuemin={0}\n aria-valuemax={100}\n >\n <div style={{ width: `${fraction * 100}%`, height: \"100%\", background: barColor }} />\n </div>\n ) : null}\n {over ? (\n <span style={{ color: \"var(--mk-primary)\", fontSize: \"0.75rem\" }}>\n Over included allotment — overage billed per usage pricing.\n </span>\n ) : null}\n </div>\n );\n}\n","import { type CSSProperties } from \"react\";\nimport { useCredits } from \"../hooks\";\nimport { useMonetizeKit } from \"../provider\";\nimport { tokensToStyle } from \"../theme/tokens\";\nimport { formatMoney } from \"../lib/format\";\nimport { UsageBanner } from \"./UsageBanner\";\n\nexport interface CustomerPortalProps {\n /** Current plan name to display. */\n planName?: string;\n /** Meters to surface usage for. */\n meterIds?: string[];\n /** Whether to show the credit balance card. */\n showCredits?: boolean;\n locale?: string;\n currency?: string;\n onManageBilling?: () => void;\n}\n\nconst containerStyle: CSSProperties = {\n border: \"1px solid var(--mk-border)\",\n borderRadius: \"var(--mk-radius)\",\n padding: \"1.25rem\",\n background: \"var(--mk-bg)\",\n color: \"var(--mk-fg)\",\n fontFamily: \"var(--mk-font)\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"1rem\",\n maxWidth: 480,\n};\n\n/** A self-service portal: current plan, usage meters, and credit balance. */\nexport function CustomerPortal({\n planName = \"Current plan\",\n meterIds = [],\n showCredits = true,\n locale,\n currency = \"USD\",\n onManageBilling,\n}: CustomerPortalProps) {\n const { tokens } = useMonetizeKit();\n const credits = useCredits();\n\n return (\n <div style={{ ...tokensToStyle(tokens), ...containerStyle }} data-mk-component=\"customer-portal\">\n <div style={{ display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\" }}>\n <div>\n <div style={{ fontSize: \"0.75rem\", color: \"var(--mk-muted)\" }}>Plan</div>\n <div style={{ fontSize: \"1.125rem\", fontWeight: 700 }}>{planName}</div>\n </div>\n <button\n type=\"button\"\n onClick={onManageBilling}\n style={{\n background: \"var(--mk-primary)\",\n color: \"var(--mk-primary-fg)\",\n border: \"none\",\n borderRadius: \"var(--mk-radius)\",\n padding: \"0.5rem 0.875rem\",\n fontWeight: 600,\n cursor: \"pointer\",\n }}\n >\n Manage billing\n </button>\n </div>\n\n {meterIds.map((meterId) => (\n <UsageBanner key={meterId} meterId={meterId} label={meterId} locale={locale} />\n ))}\n\n {showCredits ? (\n <div\n style={{\n border: \"1px solid var(--mk-border)\",\n borderRadius: \"var(--mk-radius)\",\n padding: \"0.875rem 1rem\",\n display: \"flex\",\n justifyContent: \"space-between\",\n }}\n >\n <span style={{ fontWeight: 600, fontSize: \"0.875rem\" }}>Credits</span>\n <span style={{ color: \"var(--mk-muted)\" }}>\n {credits.loading\n ? \"…\"\n : formatMoney(credits.balance, credits.currency ?? currency, locale)}\n </span>\n </div>\n ) : null}\n </div>\n );\n}\n","import type { ReactNode } from \"react\";\nimport { useEntitlement } from \"../hooks\";\n\nexport interface EntitlementGateProps {\n /** Feature key to check for the provider's customer. */\n feature: string;\n /** Rendered when the customer is entitled. */\n children: ReactNode;\n /** Rendered when not entitled (defaults to nothing). */\n fallback?: ReactNode;\n /** Rendered while the entitlement check is loading. */\n loadingFallback?: ReactNode;\n}\n\n/** Conditionally render children based on a live entitlement check. */\nexport function EntitlementGate({\n feature,\n children,\n fallback = null,\n loadingFallback = null,\n}: EntitlementGateProps) {\n const { allowed, loading } = useEntitlement(feature);\n if (loading) return <>{loadingFallback}</>;\n return <>{allowed ? children : fallback}</>;\n}\n"]}
|