@lukoweb/apitogo-plugin-dev-portal-billing 0.2.35 → 0.2.37
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/{CheckoutPage-CW_EBGog.mjs → CheckoutPage-DiYurW2O.mjs} +1 -1
- package/dist/ChoosePlanNotice-DITJNrh1.mjs +34 -0
- package/dist/{CredentialsPage-CtIEboEy.mjs → CredentialsPage-Ir8Gf62T.mjs} +4 -16
- package/dist/{PricingPage-MTslSnCh.mjs → PricingPage-DodoDd3h.mjs} +1 -1
- package/dist/{SubscriptionsPage-9lXnVks7.mjs → SubscriptionsPage-CDmzL6QX.mjs} +7 -15
- package/dist/{billing-state-pnT0H1Lm.mjs → billing-state-CIZ0pR37.mjs} +8 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +40 -5
- package/package.json +3 -3
- /package/dist/{UsagePage--mg_NzrA.mjs → UsagePage-C4K_arXh.mjs} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { o as PRICING_PATH, t as ACCOUNT_PLANS_PATH } from "./paths-ujy9wjVK.mjs";
|
|
2
2
|
import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-CgygF2z5.mjs";
|
|
3
|
-
import { t as FREE_CHECKOUT_MARKER } from "./billing-state-
|
|
3
|
+
import { t as FREE_CHECKOUT_MARKER } from "./billing-state-CIZ0pR37.mjs";
|
|
4
4
|
import { ShieldIcon } from "@lukoweb/apitogo/icons";
|
|
5
5
|
import { Link, useNavigate, useSearchParams } from "@lukoweb/apitogo/router";
|
|
6
6
|
import { useEffect } from "react";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { t as ACCOUNT_PLANS_PATH } from "./paths-ujy9wjVK.mjs";
|
|
2
|
+
import { Link } from "@lukoweb/apitogo/router";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { Button } from "@lukoweb/apitogo/ui/Button.js";
|
|
5
|
+
|
|
6
|
+
//#region src/components/ChoosePlanNotice.tsx
|
|
7
|
+
/** What a signed-in visitor without a plan reads, and the one thing to do next. Names no plan: a portal can sell only paid ones. */
|
|
8
|
+
const CHOOSE_PLAN_TITLE = "Choose a plan to get started";
|
|
9
|
+
const CHOOSE_PLAN_BODY = "An API key belongs to a plan. Choose a plan first, then come back here to create your key and call the API.";
|
|
10
|
+
const ChoosePlanNotice = ({ className = "rounded-xl border border-dashed p-8 text-center", body = CHOOSE_PLAN_BODY }) => /* @__PURE__ */ jsxs("div", {
|
|
11
|
+
className,
|
|
12
|
+
"data-testid": "choose-plan-notice",
|
|
13
|
+
children: [
|
|
14
|
+
/* @__PURE__ */ jsx("h2", {
|
|
15
|
+
className: "text-lg font-semibold",
|
|
16
|
+
children: CHOOSE_PLAN_TITLE
|
|
17
|
+
}),
|
|
18
|
+
/* @__PURE__ */ jsx("p", {
|
|
19
|
+
className: "mt-2 text-muted-foreground",
|
|
20
|
+
children: body
|
|
21
|
+
}),
|
|
22
|
+
/* @__PURE__ */ jsx(Button, {
|
|
23
|
+
className: "mt-4",
|
|
24
|
+
asChild: true,
|
|
25
|
+
children: /* @__PURE__ */ jsx(Link, {
|
|
26
|
+
to: ACCOUNT_PLANS_PATH,
|
|
27
|
+
children: "Choose a plan"
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
]
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
34
|
+
export { ChoosePlanNotice as t };
|
|
@@ -3,6 +3,7 @@ import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalA
|
|
|
3
3
|
import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
|
|
4
4
|
import { n as createDemoCredentials } from "./demo-session-Cez740bi.mjs";
|
|
5
5
|
import { r as useApiKeys, t as ApiKeysPanel } from "./ApiKeysPanel-DrArkHXf.mjs";
|
|
6
|
+
import { t as ChoosePlanNotice } from "./ChoosePlanNotice-DITJNrh1.mjs";
|
|
6
7
|
import { Head } from "@lukoweb/apitogo/components";
|
|
7
8
|
import { KeyRoundIcon } from "@lukoweb/apitogo/icons";
|
|
8
9
|
import { Link as Link$1 } from "@lukoweb/apitogo/router";
|
|
@@ -110,11 +111,11 @@ const CredentialsPage = () => {
|
|
|
110
111
|
className: "text-muted-foreground",
|
|
111
112
|
children: "Loading credentials…"
|
|
112
113
|
}) : null,
|
|
113
|
-
!isPreview && !isDemoSession && auth.isAuthenticated && (error || keysError) ? /* @__PURE__ */ jsxs("div", {
|
|
114
|
+
!isPreview && !isDemoSession && auth.isAuthenticated && (error || keysError) ? error instanceof Error && error.message === "NO_ENTITLEMENT" || keysError instanceof Error && keysError.message === "NO_ENTITLEMENT" ? /* @__PURE__ */ jsx(ChoosePlanNotice, {}) : /* @__PURE__ */ jsxs("div", {
|
|
114
115
|
className: "rounded-xl border border-dashed p-8 text-center",
|
|
115
116
|
children: [/* @__PURE__ */ jsx("p", {
|
|
116
117
|
className: "text-muted-foreground",
|
|
117
|
-
children:
|
|
118
|
+
children: "Unable to load API credentials. Try again in a moment."
|
|
118
119
|
}), /* @__PURE__ */ jsx(Button$1, {
|
|
119
120
|
className: "mt-4",
|
|
120
121
|
asChild: true,
|
|
@@ -126,20 +127,7 @@ const CredentialsPage = () => {
|
|
|
126
127
|
}) : null,
|
|
127
128
|
(isDemoSession || !isPreview) && auth.isAuthenticated && !error && !keysError && !isLoading && !keysLoading ? /* @__PURE__ */ jsxs("div", {
|
|
128
129
|
className: "space-y-6",
|
|
129
|
-
children: [plans.length === 0 ? /* @__PURE__ */
|
|
130
|
-
className: "rounded-xl border border-dashed p-8 text-center",
|
|
131
|
-
children: [/* @__PURE__ */ jsx("p", {
|
|
132
|
-
className: "text-muted-foreground",
|
|
133
|
-
children: "Choose a plan to get your first API key."
|
|
134
|
-
}), /* @__PURE__ */ jsx(Button$1, {
|
|
135
|
-
className: "mt-4",
|
|
136
|
-
asChild: true,
|
|
137
|
-
children: /* @__PURE__ */ jsx(Link$1, {
|
|
138
|
-
to: ACCOUNT_PLANS_PATH,
|
|
139
|
-
children: "View plans"
|
|
140
|
-
})
|
|
141
|
-
})]
|
|
142
|
-
}) : plans.map((plan) => /* @__PURE__ */ jsx(ApiKeysPanel, {
|
|
130
|
+
children: [plans.length === 0 ? /* @__PURE__ */ jsx(ChoosePlanNotice, {}) : plans.map((plan) => /* @__PURE__ */ jsx(ApiKeysPanel, {
|
|
143
131
|
plan,
|
|
144
132
|
header,
|
|
145
133
|
readOnly: isDemoSession
|
|
@@ -3,7 +3,7 @@ import { t as PricingCard } from "./PricingCard-Wra2FUX9.mjs";
|
|
|
3
3
|
import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
|
|
4
4
|
import { t as usePlans } from "./usePlans-DUex2tzT.mjs";
|
|
5
5
|
import { t as isPreviewPricingEnabled } from "./pricing-config-CfhRX9tk.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { a as isPricingLoading, n as buildCompareRows } from "./billing-state-CIZ0pR37.mjs";
|
|
7
7
|
import { Head, Heading } from "@lukoweb/apitogo/components";
|
|
8
8
|
import { Navigate } from "@lukoweb/apitogo/router";
|
|
9
9
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { i as CREDENTIALS_PATH, t as ACCOUNT_PLANS_PATH } from "./paths-ujy9wjVK.mjs";
|
|
2
2
|
import { c as isActiveSubscription, i as formatBillingPeriod, m as parseMonthlyQuotaCalls, o as formatPlanPrice, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-CgygF2z5.mjs";
|
|
3
|
-
import "./DevPortalBillingContext-CR83s4LO.mjs";
|
|
3
|
+
import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
|
|
4
4
|
import { t as useMySubscriptions } from "./useMySubscriptions-CLh3eFY4.mjs";
|
|
5
5
|
import { t as usePlans } from "./usePlans-DUex2tzT.mjs";
|
|
6
6
|
import { t as useUsage } from "./useUsage-Cx2GB3wg.mjs";
|
|
7
|
-
import { r as checkoutConfirmingText } from "./billing-state-
|
|
7
|
+
import { i as hasPaymentProvider, r as checkoutConfirmingText } from "./billing-state-CIZ0pR37.mjs";
|
|
8
|
+
import { t as ChoosePlanNotice } from "./ChoosePlanNotice-DITJNrh1.mjs";
|
|
8
9
|
import { Head, Spinner } from "@lukoweb/apitogo/components";
|
|
9
10
|
import { Link as Link$1, useSearchParams } from "@lukoweb/apitogo/router";
|
|
10
11
|
import { useEffect } from "react";
|
|
@@ -37,6 +38,7 @@ function useBillingPortal() {
|
|
|
37
38
|
//#endregion
|
|
38
39
|
//#region src/pages/SubscriptionsPage.tsx
|
|
39
40
|
const SubscriptionsPage = () => {
|
|
41
|
+
const { billing } = useDevPortalBillingConfig();
|
|
40
42
|
const auth = useAuth();
|
|
41
43
|
const [searchParams] = useSearchParams();
|
|
42
44
|
const checkoutSuccess = searchParams.get("checkout") === "success";
|
|
@@ -144,19 +146,9 @@ const SubscriptionsPage = () => {
|
|
|
144
146
|
}) : null
|
|
145
147
|
]
|
|
146
148
|
}) : null,
|
|
147
|
-
!isLoading && subscriptions.length === 0 ? /* @__PURE__ */
|
|
149
|
+
!isLoading && subscriptions.length === 0 ? /* @__PURE__ */ jsx(ChoosePlanNotice, {
|
|
148
150
|
className: "mb-8 rounded-xl border border-dashed p-8 text-center",
|
|
149
|
-
|
|
150
|
-
className: "text-muted-foreground",
|
|
151
|
-
children: "You do not have an active subscription yet."
|
|
152
|
-
}), /* @__PURE__ */ jsx(Button$1, {
|
|
153
|
-
className: "mt-4",
|
|
154
|
-
asChild: true,
|
|
155
|
-
children: /* @__PURE__ */ jsx(Link$1, {
|
|
156
|
-
to: ACCOUNT_PLANS_PATH,
|
|
157
|
-
children: "View plans"
|
|
158
|
-
})
|
|
159
|
-
})]
|
|
151
|
+
body: "You do not have a plan yet. Choose one to start using the API; your billing details appear here afterwards."
|
|
160
152
|
}) : null,
|
|
161
153
|
!isLoading && subscriptions.length > 1 ? /* @__PURE__ */ jsx("div", {
|
|
162
154
|
className: "mb-8 space-y-4",
|
|
@@ -196,7 +188,7 @@ const SubscriptionsPage = () => {
|
|
|
196
188
|
children: "Change plan"
|
|
197
189
|
})
|
|
198
190
|
}),
|
|
199
|
-
auth.isAuthenticated ? /* @__PURE__ */ jsx(Button$1, {
|
|
191
|
+
auth.isAuthenticated && hasPaymentProvider(billing) ? /* @__PURE__ */ jsx(Button$1, {
|
|
200
192
|
variant: "default",
|
|
201
193
|
disabled: billingPortal.isPending,
|
|
202
194
|
onClick: () => {
|
|
@@ -8,6 +8,13 @@ function isPricingLoading(state) {
|
|
|
8
8
|
return state.authPending && !state.hasPlans && !state.plansFailed;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
+
* Whether the project takes payments. A project whose plans service has billing `none` has no payment provider, so
|
|
12
|
+
* the billing page must not offer "Manage payment & invoices". An unknown provider counts as having one.
|
|
13
|
+
*/
|
|
14
|
+
function hasPaymentProvider(billing) {
|
|
15
|
+
return billing !== "none";
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
11
18
|
* The line shown on the billing page while the subscription is being confirmed after checkout. A free plan has no
|
|
12
19
|
* payment, so it must not claim one; the paid return from the payment provider carries no marker and says so.
|
|
13
20
|
*/
|
|
@@ -34,4 +41,4 @@ function buildCompareRows(input) {
|
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
//#endregion
|
|
37
|
-
export { isPricingLoading as i, buildCompareRows as n, checkoutConfirmingText as r, FREE_CHECKOUT_MARKER as t };
|
|
44
|
+
export { isPricingLoading as a, hasPaymentProvider as i, buildCompareRows as n, checkoutConfirmingText as r, FREE_CHECKOUT_MARKER as t };
|
package/dist/index.d.mts
CHANGED
|
@@ -41,7 +41,8 @@ type DevPortalBillingConfig = {
|
|
|
41
41
|
subtitle?: string;
|
|
42
42
|
compareRows?: DevPortalPricingCompareRow[];
|
|
43
43
|
faqs?: DevPortalPricingFaq[];
|
|
44
|
-
}; /**
|
|
44
|
+
}; /** Billing provider of the plans service (`none`, `stripe`, ...). `none`: no payment is offered, so no payment page. */
|
|
45
|
+
billing?: string; /** Address of the plans backend; the portal's generated wrapper passes it. The plans request starts from it early. */
|
|
45
46
|
apiBaseUrl?: string; /** Live gateway URL (publish bake or explicit plugin option). */
|
|
46
47
|
gatewayBaseUrl?: string | null; /** Local preview plan catalog (from `apitogo.config.json` `plans`). */
|
|
47
48
|
previewPlans?: Array<{
|
package/dist/index.mjs
CHANGED
|
@@ -327,15 +327,49 @@ function buildDevPortalHeaderNavigation({ navigation, pricingEnabled, authNavEna
|
|
|
327
327
|
return items;
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
+
//#endregion
|
|
331
|
+
//#region src/utils/signed-in-identity.ts
|
|
332
|
+
const SIGNED_IN_IDENTITY_ID = "signed-in-user";
|
|
333
|
+
/**
|
|
334
|
+
* The API reference's "Try it" identity for the signed-in visitor: the request carries the visitor's own token as
|
|
335
|
+
* `Authorization: Bearer`, so an API behind the gateway's token check can be tried from the docs. The token comes from
|
|
336
|
+
* the plans backend (`/api/v1/credentials`, answered from the session cookie) and is asked for when the identity list
|
|
337
|
+
* is built. A visitor who is signed out or has no subscription gets no identity, and the playground offers none.
|
|
338
|
+
*/
|
|
339
|
+
async function getSignedInIdentities(apiBaseUrl, fetchImpl = fetch) {
|
|
340
|
+
if (!apiBaseUrl) return [];
|
|
341
|
+
try {
|
|
342
|
+
const response = await fetchImpl(`${apiBaseUrl}/api/v1/credentials`, {
|
|
343
|
+
method: "GET",
|
|
344
|
+
headers: { Accept: "application/json" },
|
|
345
|
+
credentials: "include"
|
|
346
|
+
});
|
|
347
|
+
if (!response.ok) return [];
|
|
348
|
+
const credentials = await response.json();
|
|
349
|
+
const token = credentials.accessToken ?? credentials.idToken;
|
|
350
|
+
if (!token) return [];
|
|
351
|
+
return [{
|
|
352
|
+
id: SIGNED_IN_IDENTITY_ID,
|
|
353
|
+
label: "Signed-in user",
|
|
354
|
+
authorizeRequest: (request) => {
|
|
355
|
+
request.headers.set("Authorization", `Bearer ${token}`);
|
|
356
|
+
return request;
|
|
357
|
+
}
|
|
358
|
+
}];
|
|
359
|
+
} catch {
|
|
360
|
+
return [];
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
330
364
|
//#endregion
|
|
331
365
|
//#region src/DevPortalBillingPlugin.tsx
|
|
332
366
|
const AccountPlansPage$1 = lazy(() => import("./AccountPlansPage-CXOfTwlX.mjs").then((n) => n.n));
|
|
333
|
-
const CheckoutPage = lazy(() => import("./CheckoutPage-
|
|
367
|
+
const CheckoutPage = lazy(() => import("./CheckoutPage-DiYurW2O.mjs"));
|
|
334
368
|
const ContactSalesPage = lazy(() => import("./ContactSalesPage-Vjzm-RxO.mjs"));
|
|
335
|
-
const CredentialsPage = lazy(() => import("./CredentialsPage-
|
|
336
|
-
const PricingPage = lazy(() => import("./PricingPage-
|
|
337
|
-
const SubscriptionsPage = lazy(() => import("./SubscriptionsPage-
|
|
338
|
-
const UsagePage = lazy(() => import("./UsagePage
|
|
369
|
+
const CredentialsPage = lazy(() => import("./CredentialsPage-Ir8Gf62T.mjs"));
|
|
370
|
+
const PricingPage = lazy(() => import("./PricingPage-DodoDd3h.mjs"));
|
|
371
|
+
const SubscriptionsPage = lazy(() => import("./SubscriptionsPage-CDmzL6QX.mjs"));
|
|
372
|
+
const UsagePage = lazy(() => import("./UsagePage-C4K_arXh.mjs"));
|
|
339
373
|
function resolvePricingEnabled(config) {
|
|
340
374
|
if (config.__meta?.pricingEnabled !== void 0) return config.__meta.pricingEnabled;
|
|
341
375
|
return isPreviewPricingEnabled();
|
|
@@ -359,6 +393,7 @@ const devPortalBillingPlugin = createPlugin((options = {}) => {
|
|
|
359
393
|
}
|
|
360
394
|
});
|
|
361
395
|
},
|
|
396
|
+
getIdentities: () => getSignedInIdentities(resolveDevPortalApiBaseUrl(options.apiBaseUrl)),
|
|
362
397
|
getProfileMenuItems: () => [{
|
|
363
398
|
label: "My Subscriptions",
|
|
364
399
|
path: SUBSCRIPTIONS_PATH,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lukoweb/apitogo-plugin-dev-portal-billing",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.37",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/zuplo/zudoku",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"react": "19.2.4",
|
|
47
47
|
"react-dom": "19.2.4",
|
|
48
48
|
"tsdown": "0.20.3",
|
|
49
|
-
"@lukoweb/apitogo": "0.2.
|
|
50
|
-
"@lukoweb/apitogo-module-user-panel": "0.2.
|
|
49
|
+
"@lukoweb/apitogo": "0.2.37",
|
|
50
|
+
"@lukoweb/apitogo-module-user-panel": "0.2.37"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"react": ">=19.2.0",
|
|
File without changes
|