@lukoweb/apitogo-plugin-dev-portal-billing 0.1.38 → 0.1.40
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/index.d.mts +9 -2
- package/dist/index.mjs +518 -164
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import * as _lukoweb_apitogo0 from "@lukoweb/apitogo";
|
|
2
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
2
3
|
|
|
4
|
+
//#region src/pages/AccountPlansPage.d.ts
|
|
5
|
+
declare const AccountPlansPage: () => react_jsx_runtime0.JSX.Element;
|
|
6
|
+
//#endregion
|
|
3
7
|
//#region src/types.d.ts
|
|
4
8
|
type DevPortalBillingConfig = {
|
|
5
9
|
pricing?: {
|
|
@@ -8,10 +12,13 @@ type DevPortalBillingConfig = {
|
|
|
8
12
|
};
|
|
9
13
|
};
|
|
10
14
|
//#endregion
|
|
11
|
-
//#region src/
|
|
15
|
+
//#region src/paths.d.ts
|
|
12
16
|
declare const PRICING_PATH = "/pricing";
|
|
13
17
|
declare const CHECKOUT_PATH = "/checkout";
|
|
14
18
|
declare const SUBSCRIPTIONS_PATH = "/account/subscriptions";
|
|
19
|
+
declare const CREDENTIALS_PATH = "/account/credentials";
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/DevPortalBillingPlugin.d.ts
|
|
15
22
|
declare const devPortalBillingPlugin: (options?: DevPortalBillingConfig | undefined) => _lukoweb_apitogo0.ApitogoPlugin;
|
|
16
23
|
//#endregion
|
|
17
|
-
export { CHECKOUT_PATH, PRICING_PATH, SUBSCRIPTIONS_PATH, devPortalBillingPlugin };
|
|
24
|
+
export { AccountPlansPage, CHECKOUT_PATH, CREDENTIALS_PATH, PRICING_PATH, SUBSCRIPTIONS_PATH, devPortalBillingPlugin };
|
package/dist/index.mjs
CHANGED
|
@@ -1,72 +1,16 @@
|
|
|
1
|
-
import { cn, createPlugin } from "@lukoweb/apitogo";
|
|
2
|
-
import { ShieldIcon, StarsIcon } from "@lukoweb/apitogo/icons";
|
|
3
1
|
import { Button, ClientOnly, Head, Heading } from "@lukoweb/apitogo/components";
|
|
4
|
-
import {
|
|
5
|
-
import { createContext, useContext, useEffect } from "react";
|
|
6
|
-
import {
|
|
2
|
+
import { CreditCardIcon, KeyRoundIcon, ShieldIcon, StarsIcon } from "@lukoweb/apitogo/icons";
|
|
3
|
+
import { createContext, useContext, useEffect, useState } from "react";
|
|
4
|
+
import { cn, createPlugin } from "@lukoweb/apitogo";
|
|
5
|
+
import { Link, Navigate, Outlet, useNavigate, useSearchParams } from "@lukoweb/apitogo/router";
|
|
6
|
+
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@lukoweb/apitogo/ui/AlertDialog.js";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { useMutation, useQuery, useQueryClient } from "@lukoweb/apitogo/react-query";
|
|
7
9
|
import { useAuth } from "@lukoweb/apitogo/hooks";
|
|
8
|
-
import { useQuery } from "@lukoweb/apitogo/react-query";
|
|
9
10
|
import { Alert, AlertAction, AlertDescription } from "@lukoweb/apitogo/ui/Alert";
|
|
10
11
|
import { Button as Button$1 } from "@lukoweb/apitogo/ui/Button";
|
|
12
|
+
import { Secret } from "@lukoweb/apitogo/ui/Secret.js";
|
|
11
13
|
|
|
12
|
-
//#region src/DevPortalBillingContext.tsx
|
|
13
|
-
const DevPortalBillingContext = createContext({});
|
|
14
|
-
const DevPortalBillingProvider = ({ options, children }) => /* @__PURE__ */ jsx(DevPortalBillingContext.Provider, {
|
|
15
|
-
value: options,
|
|
16
|
-
children
|
|
17
|
-
});
|
|
18
|
-
const useDevPortalBillingConfig = () => useContext(DevPortalBillingContext);
|
|
19
|
-
|
|
20
|
-
//#endregion
|
|
21
|
-
//#region src/DevPortalBillingWrapper.tsx
|
|
22
|
-
const DevPortalBillingWrapper = ({ options = {} }) => /* @__PURE__ */ jsx(DevPortalBillingProvider, {
|
|
23
|
-
options,
|
|
24
|
-
children: /* @__PURE__ */ jsx(ClientOnly, { children: /* @__PURE__ */ jsx(Outlet, {}) })
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
//#endregion
|
|
28
|
-
//#region src/components/RedirectPage.tsx
|
|
29
|
-
const RedirectPage = ({ icon: Icon, title, description, url, children }) => {
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
if (url) window.location.assign(url);
|
|
32
|
-
}, [url]);
|
|
33
|
-
return /* @__PURE__ */ jsx("div", {
|
|
34
|
-
className: "flex min-h-screen items-center justify-center bg-muted",
|
|
35
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
36
|
-
className: "flex max-w-md flex-col items-center space-y-6 text-center",
|
|
37
|
-
children: [
|
|
38
|
-
/* @__PURE__ */ jsx("div", {
|
|
39
|
-
className: "relative",
|
|
40
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
41
|
-
className: "flex h-24 w-24 items-center justify-center rounded-full bg-foreground/10",
|
|
42
|
-
children: /* @__PURE__ */ jsx(Icon, { className: "h-12 w-12 text-foreground" })
|
|
43
|
-
})
|
|
44
|
-
}),
|
|
45
|
-
children,
|
|
46
|
-
/* @__PURE__ */ jsxs("div", {
|
|
47
|
-
className: "space-y-2",
|
|
48
|
-
children: [/* @__PURE__ */ jsx("h2", {
|
|
49
|
-
className: "text-2xl font-bold text-card-foreground",
|
|
50
|
-
children: title
|
|
51
|
-
}), /* @__PURE__ */ jsx("p", {
|
|
52
|
-
className: "text-muted-foreground",
|
|
53
|
-
children: description
|
|
54
|
-
})]
|
|
55
|
-
}),
|
|
56
|
-
/* @__PURE__ */ jsxs("div", {
|
|
57
|
-
className: "flex space-x-2",
|
|
58
|
-
children: [
|
|
59
|
-
/* @__PURE__ */ jsx("div", { className: "h-3 w-3 animate-pulse rounded-full bg-primary [animation-delay:-0.3s]" }),
|
|
60
|
-
/* @__PURE__ */ jsx("div", { className: "h-3 w-3 animate-pulse rounded-full bg-primary [animation-delay:-0.15s]" }),
|
|
61
|
-
/* @__PURE__ */ jsx("div", { className: "h-3 w-3 animate-pulse rounded-full bg-primary" })
|
|
62
|
-
]
|
|
63
|
-
})
|
|
64
|
-
]
|
|
65
|
-
})
|
|
66
|
-
});
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
//#endregion
|
|
70
14
|
//#region src/utils/dev-portal-api.ts
|
|
71
15
|
const DEV_PORTAL_PLACEHOLDER_API_URL = "https://dp-example.azurecontainerapps.io";
|
|
72
16
|
function resolveDevPortalApiBaseUrl(configuredUrl) {
|
|
@@ -138,6 +82,99 @@ function isActiveSubscription(status) {
|
|
|
138
82
|
return status.toLowerCase() === "active";
|
|
139
83
|
}
|
|
140
84
|
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/components/PricingCard.tsx
|
|
87
|
+
const PricingCard = ({ plan, mode = "public", subscriptionStatus, isSubscribed = false, onUnsubscribe, isCanceling = false, cancelDisabled = false }) => {
|
|
88
|
+
const limitsSummary = summarizeLimits(plan.limitsJson);
|
|
89
|
+
const isActive = isSubscribed || isActiveSubscription(subscriptionStatus ?? "");
|
|
90
|
+
return /* @__PURE__ */ jsxs("article", {
|
|
91
|
+
className: cn("flex w-full max-w-sm flex-col rounded-xl border bg-card/60 p-6 shadow-xs", isActive && mode === "account" && "border-primary/40 ring-1 ring-primary/20"),
|
|
92
|
+
"data-testid": `pricing-card-${plan.sku}`,
|
|
93
|
+
children: [
|
|
94
|
+
/* @__PURE__ */ jsxs("div", {
|
|
95
|
+
className: "flex items-start justify-between gap-2",
|
|
96
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
97
|
+
className: "text-xl font-semibold",
|
|
98
|
+
children: plan.displayName
|
|
99
|
+
}), isActive && mode === "account" ? /* @__PURE__ */ jsx("span", {
|
|
100
|
+
className: "shrink-0 rounded-full bg-primary/10 px-3 py-1 text-xs font-medium text-primary",
|
|
101
|
+
"data-testid": `subscribed-badge-${plan.sku}`,
|
|
102
|
+
children: "Subscribed"
|
|
103
|
+
}) : null]
|
|
104
|
+
}),
|
|
105
|
+
/* @__PURE__ */ jsx("p", {
|
|
106
|
+
className: "mt-2 text-3xl font-bold",
|
|
107
|
+
children: formatPlanPrice(plan)
|
|
108
|
+
}),
|
|
109
|
+
/* @__PURE__ */ jsx("p", {
|
|
110
|
+
className: "mt-1 text-sm text-muted-foreground",
|
|
111
|
+
children: formatBillingPeriod(plan.billingPeriod)
|
|
112
|
+
}),
|
|
113
|
+
limitsSummary ? /* @__PURE__ */ jsx("p", {
|
|
114
|
+
className: "mt-4 text-sm text-muted-foreground",
|
|
115
|
+
children: limitsSummary
|
|
116
|
+
}) : null,
|
|
117
|
+
/* @__PURE__ */ jsx("div", {
|
|
118
|
+
className: "mt-6 space-y-2",
|
|
119
|
+
children: mode === "account" ? isActive ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
120
|
+
/* @__PURE__ */ jsx(Button, {
|
|
121
|
+
variant: "secondary",
|
|
122
|
+
className: "w-full",
|
|
123
|
+
disabled: true,
|
|
124
|
+
children: "Current plan"
|
|
125
|
+
}),
|
|
126
|
+
onUnsubscribe ? /* @__PURE__ */ jsxs(AlertDialog, { children: [/* @__PURE__ */ jsx(AlertDialogTrigger, {
|
|
127
|
+
asChild: true,
|
|
128
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
129
|
+
variant: "outline",
|
|
130
|
+
className: "w-full",
|
|
131
|
+
disabled: cancelDisabled || isCanceling,
|
|
132
|
+
"data-testid": `unsubscribe-${plan.sku}`,
|
|
133
|
+
children: isCanceling ? "Canceling…" : "Unsubscribe"
|
|
134
|
+
})
|
|
135
|
+
}), /* @__PURE__ */ jsxs(AlertDialogContent, { children: [/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [/* @__PURE__ */ jsxs(AlertDialogTitle, { children: [
|
|
136
|
+
"Unsubscribe from ",
|
|
137
|
+
plan.displayName,
|
|
138
|
+
"?"
|
|
139
|
+
] }), /* @__PURE__ */ jsx(AlertDialogDescription, { children: "Your API access for this plan will be revoked. You can subscribe again later." })] }), /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [/* @__PURE__ */ jsx(AlertDialogCancel, { children: "Keep subscription" }), /* @__PURE__ */ jsx(AlertDialogAction, {
|
|
140
|
+
onClick: () => onUnsubscribe(plan.sku),
|
|
141
|
+
disabled: isCanceling,
|
|
142
|
+
children: "Unsubscribe"
|
|
143
|
+
})] })] })] }) : null,
|
|
144
|
+
cancelDisabled ? /* @__PURE__ */ jsx("p", {
|
|
145
|
+
className: "text-center text-xs text-muted-foreground",
|
|
146
|
+
children: "Unsubscribe is available after the portal is published."
|
|
147
|
+
}) : null
|
|
148
|
+
] }) : /* @__PURE__ */ jsx(Button, {
|
|
149
|
+
variant: "default",
|
|
150
|
+
className: "w-full",
|
|
151
|
+
asChild: true,
|
|
152
|
+
children: /* @__PURE__ */ jsx(Link, {
|
|
153
|
+
to: `/checkout?planSku=${encodeURIComponent(plan.sku)}`,
|
|
154
|
+
children: plan.isFree ? "Get started" : "Subscribe"
|
|
155
|
+
})
|
|
156
|
+
}) : isActive ? /* @__PURE__ */ jsx(Button, {
|
|
157
|
+
variant: "secondary",
|
|
158
|
+
className: "w-full",
|
|
159
|
+
asChild: true,
|
|
160
|
+
children: /* @__PURE__ */ jsx(Link, {
|
|
161
|
+
to: "/account/subscriptions",
|
|
162
|
+
children: "Manage subscription"
|
|
163
|
+
})
|
|
164
|
+
}) : /* @__PURE__ */ jsx(Button, {
|
|
165
|
+
variant: "default",
|
|
166
|
+
className: "w-full",
|
|
167
|
+
asChild: true,
|
|
168
|
+
children: /* @__PURE__ */ jsx(Link, {
|
|
169
|
+
to: `/checkout?planSku=${encodeURIComponent(plan.sku)}`,
|
|
170
|
+
children: plan.isFree ? "Get started" : "Subscribe"
|
|
171
|
+
})
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
]
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
|
|
141
178
|
//#endregion
|
|
142
179
|
//#region src/hooks/useDevPortalApiBaseUrl.ts
|
|
143
180
|
function useDevPortalApiBaseUrl() {
|
|
@@ -152,8 +189,221 @@ function useDevPortalPreviewMode() {
|
|
|
152
189
|
return isPlaceholderDevPortalApiUrl(apiBaseUrl);
|
|
153
190
|
}
|
|
154
191
|
|
|
192
|
+
//#endregion
|
|
193
|
+
//#region src/hooks/useCancelSubscription.ts
|
|
194
|
+
function useCancelSubscription() {
|
|
195
|
+
const apiBaseUrl = useDevPortalApiBaseUrl();
|
|
196
|
+
const queryClient = useQueryClient();
|
|
197
|
+
return useMutation({
|
|
198
|
+
mutationFn: async (planSku) => {
|
|
199
|
+
if (!apiBaseUrl) throw new Error("Dev portal API is not configured.");
|
|
200
|
+
const response = await fetch(`${apiBaseUrl}/api/v1/subscriptions/cancel`, {
|
|
201
|
+
method: "POST",
|
|
202
|
+
headers: {
|
|
203
|
+
Accept: "application/json",
|
|
204
|
+
"Content-Type": "application/json"
|
|
205
|
+
},
|
|
206
|
+
credentials: "include",
|
|
207
|
+
body: JSON.stringify({ planSku })
|
|
208
|
+
});
|
|
209
|
+
if (!response.ok) {
|
|
210
|
+
const detail = await response.text();
|
|
211
|
+
throw new Error(detail || "Failed to cancel subscription.");
|
|
212
|
+
}
|
|
213
|
+
return await response.json();
|
|
214
|
+
},
|
|
215
|
+
onSuccess: async () => {
|
|
216
|
+
await queryClient.invalidateQueries({ queryKey: ["dev-portal-me", apiBaseUrl] });
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
//#endregion
|
|
222
|
+
//#region src/hooks/useMySubscriptions.ts
|
|
223
|
+
function useMySubscriptions() {
|
|
224
|
+
const auth = useAuth();
|
|
225
|
+
const apiBaseUrl = useDevPortalApiBaseUrl();
|
|
226
|
+
const isPreview = useDevPortalPreviewMode();
|
|
227
|
+
return useQuery({
|
|
228
|
+
queryKey: ["dev-portal-me", apiBaseUrl],
|
|
229
|
+
enabled: auth.isAuthenticated && !isPreview && Boolean(apiBaseUrl),
|
|
230
|
+
queryFn: async () => {
|
|
231
|
+
const response = await fetch(`${apiBaseUrl}/api/v1/auth/me`, {
|
|
232
|
+
method: "GET",
|
|
233
|
+
headers: { Accept: "application/json" },
|
|
234
|
+
credentials: "include"
|
|
235
|
+
});
|
|
236
|
+
if (!response.ok) throw new Error("Failed to load subscription status.");
|
|
237
|
+
return await response.json();
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
//#endregion
|
|
243
|
+
//#region src/hooks/usePlans.ts
|
|
244
|
+
function usePlans() {
|
|
245
|
+
const apiBaseUrl = useDevPortalApiBaseUrl();
|
|
246
|
+
const isPreview = useDevPortalPreviewMode();
|
|
247
|
+
return useQuery({
|
|
248
|
+
queryKey: [
|
|
249
|
+
"dev-portal-plans",
|
|
250
|
+
apiBaseUrl,
|
|
251
|
+
isPreview
|
|
252
|
+
],
|
|
253
|
+
queryFn: async () => {
|
|
254
|
+
if (isPreview) return parseMockPlans();
|
|
255
|
+
if (!apiBaseUrl) return { plans: [] };
|
|
256
|
+
const response = await fetch(`${apiBaseUrl}/api/v1/plans`, { headers: { Accept: "application/json" } });
|
|
257
|
+
if (!response.ok) throw new Error("Failed to load plans.");
|
|
258
|
+
return await response.json();
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
//#endregion
|
|
264
|
+
//#region src/pages/AccountPlansPage.tsx
|
|
265
|
+
const AccountPlansPage = () => {
|
|
266
|
+
const isPreview = useDevPortalPreviewMode();
|
|
267
|
+
const { data: catalog, isLoading, isError } = usePlans();
|
|
268
|
+
const { data: me } = useMySubscriptions();
|
|
269
|
+
const cancelMutation = useCancelSubscription();
|
|
270
|
+
const [cancelingSku, setCancelingSku] = useState(null);
|
|
271
|
+
const subscriptionBySku = new Map((me?.subscriptions ?? []).map((sub) => [sub.planSku, sub]));
|
|
272
|
+
const handleUnsubscribe = async (planSku) => {
|
|
273
|
+
setCancelingSku(planSku);
|
|
274
|
+
try {
|
|
275
|
+
await cancelMutation.mutateAsync(planSku);
|
|
276
|
+
} finally {
|
|
277
|
+
setCancelingSku(null);
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
281
|
+
className: "mx-auto max-w-screen-xl px-4 pb-16 pt-(--padding-content-top) lg:px-8",
|
|
282
|
+
children: [
|
|
283
|
+
/* @__PURE__ */ jsx(Head, { children: /* @__PURE__ */ jsx("title", { children: "Plans & billing" }) }),
|
|
284
|
+
/* @__PURE__ */ jsxs("div", {
|
|
285
|
+
className: "mb-8 flex items-start gap-4",
|
|
286
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
287
|
+
className: "flex size-12 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary",
|
|
288
|
+
children: /* @__PURE__ */ jsx(CreditCardIcon, { size: 24 })
|
|
289
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
290
|
+
className: "space-y-1",
|
|
291
|
+
children: [/* @__PURE__ */ jsx("h1", {
|
|
292
|
+
className: "text-3xl font-semibold tracking-tight",
|
|
293
|
+
children: "Plans & billing"
|
|
294
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
295
|
+
className: "max-w-2xl text-muted-foreground",
|
|
296
|
+
children: "Compare plans, manage your subscriptions, and change tiers."
|
|
297
|
+
})]
|
|
298
|
+
})]
|
|
299
|
+
}),
|
|
300
|
+
isPreview ? /* @__PURE__ */ jsx("div", {
|
|
301
|
+
className: "mb-8 rounded-xl border border-dashed bg-muted/20 p-6 text-center text-sm text-muted-foreground",
|
|
302
|
+
children: "Local preview uses mock plans. Subscribe and unsubscribe activate after the portal is published."
|
|
303
|
+
}) : null,
|
|
304
|
+
cancelMutation.isError ? /* @__PURE__ */ jsx("div", {
|
|
305
|
+
className: "mb-6 rounded-xl border border-destructive/30 bg-destructive/10 p-4 text-sm text-destructive",
|
|
306
|
+
children: cancelMutation.error instanceof Error ? cancelMutation.error.message : "Failed to cancel subscription."
|
|
307
|
+
}) : null,
|
|
308
|
+
isLoading ? /* @__PURE__ */ jsx("p", {
|
|
309
|
+
className: "text-muted-foreground",
|
|
310
|
+
children: "Loading plans…"
|
|
311
|
+
}) : null,
|
|
312
|
+
isError ? /* @__PURE__ */ jsx("p", {
|
|
313
|
+
className: "text-destructive",
|
|
314
|
+
children: "Unable to load plans."
|
|
315
|
+
}) : null,
|
|
316
|
+
catalog && catalog.plans.length > 0 ? /* @__PURE__ */ jsx("div", {
|
|
317
|
+
className: "grid grid-cols-1 justify-center gap-6 sm:grid-cols-[repeat(auto-fit,minmax(280px,max-content))]",
|
|
318
|
+
children: catalog.plans.map((plan) => {
|
|
319
|
+
const subscription = subscriptionBySku.get(plan.sku);
|
|
320
|
+
const isSubscribed = isActiveSubscription(subscription?.status ?? "");
|
|
321
|
+
return /* @__PURE__ */ jsx(PricingCard, {
|
|
322
|
+
plan,
|
|
323
|
+
mode: "account",
|
|
324
|
+
subscriptionStatus: subscription?.status,
|
|
325
|
+
isSubscribed,
|
|
326
|
+
onUnsubscribe: isPreview ? void 0 : handleUnsubscribe,
|
|
327
|
+
isCanceling: cancelingSku === plan.sku,
|
|
328
|
+
cancelDisabled: isPreview
|
|
329
|
+
}, plan.sku);
|
|
330
|
+
})
|
|
331
|
+
}) : !isLoading && !isError ? /* @__PURE__ */ jsx("div", {
|
|
332
|
+
className: "rounded-xl border border-dashed p-8 text-center text-sm text-muted-foreground",
|
|
333
|
+
children: "No plans are available yet. Sync plans from your APItoGo manifest or platform gateway."
|
|
334
|
+
}) : null
|
|
335
|
+
]
|
|
336
|
+
});
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
//#endregion
|
|
340
|
+
//#region src/DevPortalBillingContext.tsx
|
|
341
|
+
const DevPortalBillingContext = createContext({});
|
|
342
|
+
const DevPortalBillingProvider = ({ options, children }) => /* @__PURE__ */ jsx(DevPortalBillingContext.Provider, {
|
|
343
|
+
value: options,
|
|
344
|
+
children
|
|
345
|
+
});
|
|
346
|
+
const useDevPortalBillingConfig = () => useContext(DevPortalBillingContext);
|
|
347
|
+
|
|
348
|
+
//#endregion
|
|
349
|
+
//#region src/DevPortalBillingWrapper.tsx
|
|
350
|
+
const DevPortalBillingWrapper = ({ options = {} }) => /* @__PURE__ */ jsx(DevPortalBillingProvider, {
|
|
351
|
+
options,
|
|
352
|
+
children: /* @__PURE__ */ jsx(ClientOnly, { children: /* @__PURE__ */ jsx(Outlet, {}) })
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
//#endregion
|
|
356
|
+
//#region src/components/RedirectPage.tsx
|
|
357
|
+
const RedirectPage = ({ icon: Icon, title, description, url, children }) => {
|
|
358
|
+
useEffect(() => {
|
|
359
|
+
if (url) window.location.assign(url);
|
|
360
|
+
}, [url]);
|
|
361
|
+
return /* @__PURE__ */ jsx("div", {
|
|
362
|
+
className: "flex min-h-screen items-center justify-center bg-muted",
|
|
363
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
364
|
+
className: "flex max-w-md flex-col items-center space-y-6 text-center",
|
|
365
|
+
children: [
|
|
366
|
+
/* @__PURE__ */ jsx("div", {
|
|
367
|
+
className: "relative",
|
|
368
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
369
|
+
className: "flex h-24 w-24 items-center justify-center rounded-full bg-foreground/10",
|
|
370
|
+
children: /* @__PURE__ */ jsx(Icon, { className: "h-12 w-12 text-foreground" })
|
|
371
|
+
})
|
|
372
|
+
}),
|
|
373
|
+
children,
|
|
374
|
+
/* @__PURE__ */ jsxs("div", {
|
|
375
|
+
className: "space-y-2",
|
|
376
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
377
|
+
className: "text-2xl font-bold text-card-foreground",
|
|
378
|
+
children: title
|
|
379
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
380
|
+
className: "text-muted-foreground",
|
|
381
|
+
children: description
|
|
382
|
+
})]
|
|
383
|
+
}),
|
|
384
|
+
/* @__PURE__ */ jsxs("div", {
|
|
385
|
+
className: "flex space-x-2",
|
|
386
|
+
children: [
|
|
387
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-3 animate-pulse rounded-full bg-primary [animation-delay:-0.3s]" }),
|
|
388
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-3 animate-pulse rounded-full bg-primary [animation-delay:-0.15s]" }),
|
|
389
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-3 animate-pulse rounded-full bg-primary" })
|
|
390
|
+
]
|
|
391
|
+
})
|
|
392
|
+
]
|
|
393
|
+
})
|
|
394
|
+
});
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
//#endregion
|
|
398
|
+
//#region src/paths.ts
|
|
399
|
+
const PRICING_PATH = "/pricing";
|
|
400
|
+
const CHECKOUT_PATH = "/checkout";
|
|
401
|
+
const SUBSCRIPTIONS_PATH = "/account/subscriptions";
|
|
402
|
+
const CREDENTIALS_PATH = "/account/credentials";
|
|
403
|
+
|
|
155
404
|
//#endregion
|
|
156
405
|
//#region src/pages/CheckoutPage.tsx
|
|
406
|
+
const ACCOUNT_PLANS_PATH = "/account/plans";
|
|
157
407
|
const CheckoutPage = () => {
|
|
158
408
|
const [searchParams] = useSearchParams();
|
|
159
409
|
const planSku = searchParams.get("planSku") ?? "";
|
|
@@ -161,6 +411,7 @@ const CheckoutPage = () => {
|
|
|
161
411
|
const auth = useAuth();
|
|
162
412
|
const apiBaseUrl = useDevPortalApiBaseUrl();
|
|
163
413
|
const isPreview = useDevPortalPreviewMode();
|
|
414
|
+
const plansPath = auth.isAuthenticated ? ACCOUNT_PLANS_PATH : PRICING_PATH;
|
|
164
415
|
const checkoutQuery = useQuery({
|
|
165
416
|
queryKey: [
|
|
166
417
|
"dev-portal-checkout",
|
|
@@ -197,9 +448,9 @@ const CheckoutPage = () => {
|
|
|
197
448
|
"Missing plan SKU.",
|
|
198
449
|
" ",
|
|
199
450
|
/* @__PURE__ */ jsx(Link, {
|
|
200
|
-
to:
|
|
451
|
+
to: plansPath,
|
|
201
452
|
className: "text-primary underline",
|
|
202
|
-
children: "Back to
|
|
453
|
+
children: "Back to plans"
|
|
203
454
|
})
|
|
204
455
|
]
|
|
205
456
|
});
|
|
@@ -218,8 +469,8 @@ const CheckoutPage = () => {
|
|
|
218
469
|
asChild: true,
|
|
219
470
|
variant: "outline",
|
|
220
471
|
children: /* @__PURE__ */ jsx(Link, {
|
|
221
|
-
to:
|
|
222
|
-
children: "Back to
|
|
472
|
+
to: plansPath,
|
|
473
|
+
children: "Back to plans"
|
|
223
474
|
})
|
|
224
475
|
})
|
|
225
476
|
]
|
|
@@ -240,8 +491,8 @@ const CheckoutPage = () => {
|
|
|
240
491
|
size: "xs",
|
|
241
492
|
asChild: true,
|
|
242
493
|
children: /* @__PURE__ */ jsx(Link, {
|
|
243
|
-
to:
|
|
244
|
-
children: "Back to
|
|
494
|
+
to: plansPath,
|
|
495
|
+
children: "Back to plans"
|
|
245
496
|
})
|
|
246
497
|
}) })]
|
|
247
498
|
}) : null
|
|
@@ -249,94 +500,181 @@ const CheckoutPage = () => {
|
|
|
249
500
|
};
|
|
250
501
|
|
|
251
502
|
//#endregion
|
|
252
|
-
//#region src/
|
|
253
|
-
|
|
254
|
-
const limitsSummary = summarizeLimits(plan.limitsJson);
|
|
255
|
-
return /* @__PURE__ */ jsxs("article", {
|
|
256
|
-
className: cn("flex w-full max-w-sm flex-col rounded-xl border bg-card/60 p-6 shadow-xs"),
|
|
257
|
-
"data-testid": `pricing-card-${plan.sku}`,
|
|
258
|
-
children: [
|
|
259
|
-
/* @__PURE__ */ jsx("h2", {
|
|
260
|
-
className: "text-xl font-semibold",
|
|
261
|
-
children: plan.displayName
|
|
262
|
-
}),
|
|
263
|
-
/* @__PURE__ */ jsx("p", {
|
|
264
|
-
className: "mt-2 text-3xl font-bold",
|
|
265
|
-
children: formatPlanPrice(plan)
|
|
266
|
-
}),
|
|
267
|
-
/* @__PURE__ */ jsx("p", {
|
|
268
|
-
className: "mt-1 text-sm text-muted-foreground",
|
|
269
|
-
children: formatBillingPeriod(plan.billingPeriod)
|
|
270
|
-
}),
|
|
271
|
-
limitsSummary ? /* @__PURE__ */ jsx("p", {
|
|
272
|
-
className: "mt-4 text-sm text-muted-foreground",
|
|
273
|
-
children: limitsSummary
|
|
274
|
-
}) : null,
|
|
275
|
-
/* @__PURE__ */ jsx("div", {
|
|
276
|
-
className: "mt-6",
|
|
277
|
-
children: isSubscribed ? /* @__PURE__ */ jsx(Button, {
|
|
278
|
-
variant: "secondary",
|
|
279
|
-
className: "w-full",
|
|
280
|
-
asChild: true,
|
|
281
|
-
children: /* @__PURE__ */ jsx(Link, {
|
|
282
|
-
to: "/account/subscriptions",
|
|
283
|
-
children: "Manage subscription"
|
|
284
|
-
})
|
|
285
|
-
}) : /* @__PURE__ */ jsx(Button, {
|
|
286
|
-
variant: "default",
|
|
287
|
-
className: "w-full",
|
|
288
|
-
asChild: true,
|
|
289
|
-
children: /* @__PURE__ */ jsx(Link, {
|
|
290
|
-
to: `/checkout?planSku=${encodeURIComponent(plan.sku)}`,
|
|
291
|
-
children: plan.isFree ? "Get started" : "Subscribe"
|
|
292
|
-
})
|
|
293
|
-
})
|
|
294
|
-
})
|
|
295
|
-
]
|
|
296
|
-
});
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
//#endregion
|
|
300
|
-
//#region src/hooks/useMySubscriptions.ts
|
|
301
|
-
function useMySubscriptions() {
|
|
503
|
+
//#region src/hooks/useCredentials.ts
|
|
504
|
+
function useCredentials() {
|
|
302
505
|
const auth = useAuth();
|
|
303
506
|
const apiBaseUrl = useDevPortalApiBaseUrl();
|
|
304
507
|
const isPreview = useDevPortalPreviewMode();
|
|
305
508
|
return useQuery({
|
|
306
|
-
queryKey: ["dev-portal-
|
|
509
|
+
queryKey: ["dev-portal-credentials", apiBaseUrl],
|
|
307
510
|
enabled: auth.isAuthenticated && !isPreview && Boolean(apiBaseUrl),
|
|
308
511
|
queryFn: async () => {
|
|
309
|
-
const response = await fetch(`${apiBaseUrl}/api/v1/
|
|
512
|
+
const response = await fetch(`${apiBaseUrl}/api/v1/credentials`, {
|
|
310
513
|
method: "GET",
|
|
311
514
|
headers: { Accept: "application/json" },
|
|
312
515
|
credentials: "include"
|
|
313
516
|
});
|
|
314
|
-
if (
|
|
517
|
+
if (response.status === 404) throw new Error("NO_ENTITLEMENT");
|
|
518
|
+
if (!response.ok) throw new Error("Failed to load API credentials.");
|
|
315
519
|
return await response.json();
|
|
316
|
-
}
|
|
520
|
+
},
|
|
521
|
+
retry: (failureCount, error) => error instanceof Error && error.message === "NO_ENTITLEMENT" ? false : failureCount < 2
|
|
317
522
|
});
|
|
318
523
|
}
|
|
319
524
|
|
|
320
525
|
//#endregion
|
|
321
|
-
//#region src/
|
|
322
|
-
|
|
323
|
-
const
|
|
526
|
+
//#region src/pages/CredentialsPage.tsx
|
|
527
|
+
const CredentialsPage = () => {
|
|
528
|
+
const auth = useAuth();
|
|
324
529
|
const isPreview = useDevPortalPreviewMode();
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
530
|
+
const { data: credentials, isLoading, error } = useCredentials();
|
|
531
|
+
const { data: catalog } = usePlans();
|
|
532
|
+
const [revealed, setRevealed] = useState(false);
|
|
533
|
+
const plan = catalog?.plans.find((item) => item.sku === credentials?.planSku);
|
|
534
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
535
|
+
className: "mx-auto max-w-screen-lg px-4 pb-16 pt-(--padding-content-top) lg:px-8",
|
|
536
|
+
children: [
|
|
537
|
+
/* @__PURE__ */ jsx(Head, { children: /* @__PURE__ */ jsx("title", { children: "API credentials" }) }),
|
|
538
|
+
/* @__PURE__ */ jsxs("div", {
|
|
539
|
+
className: "mb-8 flex items-start gap-4",
|
|
540
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
541
|
+
className: "flex size-12 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary",
|
|
542
|
+
children: /* @__PURE__ */ jsx(KeyRoundIcon, { size: 24 })
|
|
543
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
544
|
+
className: "space-y-1",
|
|
545
|
+
children: [/* @__PURE__ */ jsx("h1", {
|
|
546
|
+
className: "text-3xl font-semibold tracking-tight",
|
|
547
|
+
children: "API credentials"
|
|
548
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
549
|
+
className: "text-muted-foreground",
|
|
550
|
+
children: "Use your subscription key to authenticate requests to the API gateway."
|
|
551
|
+
})]
|
|
552
|
+
})]
|
|
553
|
+
}),
|
|
554
|
+
isPreview ? /* @__PURE__ */ jsx("div", {
|
|
555
|
+
className: "rounded-xl border border-dashed p-8 text-center",
|
|
556
|
+
children: /* @__PURE__ */ jsx("p", {
|
|
557
|
+
className: "text-muted-foreground",
|
|
558
|
+
children: "API credentials are available after the developer portal is published."
|
|
559
|
+
})
|
|
560
|
+
}) : null,
|
|
561
|
+
!isPreview && !auth.isAuthenticated ? /* @__PURE__ */ jsxs("div", {
|
|
562
|
+
className: "rounded-xl border border-dashed p-8 text-center",
|
|
563
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
564
|
+
className: "text-muted-foreground",
|
|
565
|
+
children: "Sign in to view your subscription key."
|
|
566
|
+
}), /* @__PURE__ */ jsx(Button$1, {
|
|
567
|
+
className: "mt-4",
|
|
568
|
+
asChild: true,
|
|
569
|
+
children: /* @__PURE__ */ jsx(Link, {
|
|
570
|
+
to: "/account/plans",
|
|
571
|
+
children: "View plans"
|
|
572
|
+
})
|
|
573
|
+
})]
|
|
574
|
+
}) : null,
|
|
575
|
+
!isPreview && auth.isAuthenticated && isLoading ? /* @__PURE__ */ jsx("p", {
|
|
576
|
+
className: "text-muted-foreground",
|
|
577
|
+
children: "Loading credentials…"
|
|
578
|
+
}) : null,
|
|
579
|
+
!isPreview && auth.isAuthenticated && error ? /* @__PURE__ */ jsxs("div", {
|
|
580
|
+
className: "rounded-xl border border-dashed p-8 text-center",
|
|
581
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
582
|
+
className: "text-muted-foreground",
|
|
583
|
+
children: error instanceof Error && error.message === "NO_ENTITLEMENT" ? "No active subscription found. Choose a plan to get your API key." : "Unable to load API credentials. Try again in a moment."
|
|
584
|
+
}), /* @__PURE__ */ jsx(Button$1, {
|
|
585
|
+
className: "mt-4",
|
|
586
|
+
asChild: true,
|
|
587
|
+
children: /* @__PURE__ */ jsx(Link, {
|
|
588
|
+
to: "/account/plans",
|
|
589
|
+
children: "View plans"
|
|
590
|
+
})
|
|
591
|
+
})]
|
|
592
|
+
}) : null,
|
|
593
|
+
!isPreview && credentials ? /* @__PURE__ */ jsxs("div", {
|
|
594
|
+
className: "space-y-6",
|
|
595
|
+
children: [
|
|
596
|
+
/* @__PURE__ */ jsx("article", {
|
|
597
|
+
className: "rounded-xl border bg-card/60 p-6",
|
|
598
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
599
|
+
className: "space-y-1",
|
|
600
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
601
|
+
className: "text-lg font-medium",
|
|
602
|
+
children: "Active plan"
|
|
603
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
604
|
+
className: "text-sm text-muted-foreground",
|
|
605
|
+
children: plan?.displayName ?? credentials.planSku
|
|
606
|
+
})]
|
|
607
|
+
})
|
|
608
|
+
}),
|
|
609
|
+
/* @__PURE__ */ jsxs("article", {
|
|
610
|
+
className: "rounded-xl border bg-card/60 p-6 space-y-4",
|
|
611
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
612
|
+
className: "space-y-1",
|
|
613
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
614
|
+
className: "text-lg font-medium",
|
|
615
|
+
children: "Subscription key"
|
|
616
|
+
}), /* @__PURE__ */ jsxs("p", {
|
|
617
|
+
className: "text-sm text-muted-foreground",
|
|
618
|
+
children: [
|
|
619
|
+
"Send this value in the",
|
|
620
|
+
" ",
|
|
621
|
+
/* @__PURE__ */ jsx("code", {
|
|
622
|
+
className: "rounded bg-muted px-1 py-0.5 text-xs",
|
|
623
|
+
children: "Ocp-Apim-Subscription-Key"
|
|
624
|
+
}),
|
|
625
|
+
" ",
|
|
626
|
+
"header when calling the gateway."
|
|
627
|
+
]
|
|
628
|
+
})]
|
|
629
|
+
}), /* @__PURE__ */ jsx(Secret, {
|
|
630
|
+
className: "max-w-full w-full",
|
|
631
|
+
secret: credentials.subscriptionKey,
|
|
632
|
+
status: "active",
|
|
633
|
+
revealed,
|
|
634
|
+
onReveal: setRevealed
|
|
635
|
+
})]
|
|
636
|
+
}),
|
|
637
|
+
/* @__PURE__ */ jsxs("article", {
|
|
638
|
+
className: "rounded-xl border bg-card/60 p-6 space-y-4",
|
|
639
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
640
|
+
className: "space-y-1",
|
|
641
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
642
|
+
className: "text-lg font-medium",
|
|
643
|
+
children: "Gateway URL"
|
|
644
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
645
|
+
className: "text-sm text-muted-foreground",
|
|
646
|
+
children: "Base URL for API requests through the gateway."
|
|
647
|
+
})]
|
|
648
|
+
}), /* @__PURE__ */ jsx(Secret, {
|
|
649
|
+
className: "max-w-full w-full",
|
|
650
|
+
secret: credentials.gatewayBaseUrl,
|
|
651
|
+
status: "none",
|
|
652
|
+
noReveal: true
|
|
653
|
+
})]
|
|
654
|
+
})
|
|
655
|
+
]
|
|
656
|
+
}) : null,
|
|
657
|
+
/* @__PURE__ */ jsxs("div", {
|
|
658
|
+
className: "mt-8 flex flex-wrap gap-3",
|
|
659
|
+
children: [/* @__PURE__ */ jsx(Button$1, {
|
|
660
|
+
variant: "outline",
|
|
661
|
+
asChild: true,
|
|
662
|
+
children: /* @__PURE__ */ jsx(Link, {
|
|
663
|
+
to: "/account/subscriptions",
|
|
664
|
+
children: "My subscriptions"
|
|
665
|
+
})
|
|
666
|
+
}), /* @__PURE__ */ jsx(Button$1, {
|
|
667
|
+
variant: "outline",
|
|
668
|
+
asChild: true,
|
|
669
|
+
children: /* @__PURE__ */ jsx(Link, {
|
|
670
|
+
to: "/api",
|
|
671
|
+
children: "API reference"
|
|
672
|
+
})
|
|
673
|
+
})]
|
|
674
|
+
})
|
|
675
|
+
]
|
|
338
676
|
});
|
|
339
|
-
}
|
|
677
|
+
};
|
|
340
678
|
|
|
341
679
|
//#endregion
|
|
342
680
|
//#region src/pages/PricingPage.tsx
|
|
@@ -346,6 +684,10 @@ const PricingPage = () => {
|
|
|
346
684
|
const isPreview = useDevPortalPreviewMode();
|
|
347
685
|
const { data: catalog, isLoading, isError } = usePlans();
|
|
348
686
|
const { data: me } = useMySubscriptions();
|
|
687
|
+
if (auth.isAuthenticated) return /* @__PURE__ */ jsx(Navigate, {
|
|
688
|
+
to: "/account/plans",
|
|
689
|
+
replace: true
|
|
690
|
+
});
|
|
349
691
|
const activeSkus = new Set((me?.subscriptions ?? []).filter((sub) => isActiveSubscription(sub.status)).map((sub) => sub.planSku));
|
|
350
692
|
const title = pricing?.title ?? "Pricing";
|
|
351
693
|
const subtitle = pricing?.subtitle ?? "Compare plans and subscribe when you are ready to integrate.";
|
|
@@ -392,22 +734,12 @@ const PricingPage = () => {
|
|
|
392
734
|
className: "grid grid-cols-1 justify-center gap-6 sm:grid-cols-[repeat(auto-fit,minmax(280px,max-content))]",
|
|
393
735
|
children: catalog.plans.map((plan) => /* @__PURE__ */ jsx(PricingCard, {
|
|
394
736
|
plan,
|
|
737
|
+
mode: "public",
|
|
395
738
|
isSubscribed: activeSkus.has(plan.sku)
|
|
396
739
|
}, plan.sku))
|
|
397
740
|
}) : !isLoading && !isError ? /* @__PURE__ */ jsx("div", {
|
|
398
741
|
className: "mx-auto max-w-lg rounded-xl border border-dashed p-8 text-center text-sm text-muted-foreground",
|
|
399
742
|
children: "No plans are available yet. Sync plans from your APItoGo manifest or platform gateway."
|
|
400
|
-
}) : null,
|
|
401
|
-
auth.isAuthenticated ? /* @__PURE__ */ jsx("div", {
|
|
402
|
-
className: "mt-10 text-center",
|
|
403
|
-
children: /* @__PURE__ */ jsx(Button$1, {
|
|
404
|
-
variant: "outline",
|
|
405
|
-
asChild: true,
|
|
406
|
-
children: /* @__PURE__ */ jsx(Link, {
|
|
407
|
-
to: "/account/plans",
|
|
408
|
-
children: "Manage billing"
|
|
409
|
-
})
|
|
410
|
-
})
|
|
411
743
|
}) : null
|
|
412
744
|
]
|
|
413
745
|
});
|
|
@@ -437,9 +769,18 @@ const SubscriptionsPage = () => {
|
|
|
437
769
|
children: "Review your active plans and manage billing."
|
|
438
770
|
})]
|
|
439
771
|
}),
|
|
440
|
-
checkoutSuccess ? /* @__PURE__ */
|
|
772
|
+
checkoutSuccess ? /* @__PURE__ */ jsxs("div", {
|
|
441
773
|
className: "mb-6 rounded-xl border border-green-500/30 bg-green-500/10 p-4 text-sm",
|
|
442
|
-
children:
|
|
774
|
+
children: [
|
|
775
|
+
"Your subscription is active.",
|
|
776
|
+
" ",
|
|
777
|
+
/* @__PURE__ */ jsx(Link, {
|
|
778
|
+
to: CREDENTIALS_PATH,
|
|
779
|
+
className: "font-medium underline",
|
|
780
|
+
children: "View your API credentials"
|
|
781
|
+
}),
|
|
782
|
+
"."
|
|
783
|
+
]
|
|
443
784
|
}) : null,
|
|
444
785
|
isLoading ? /* @__PURE__ */ jsx("p", {
|
|
445
786
|
className: "text-muted-foreground",
|
|
@@ -454,7 +795,7 @@ const SubscriptionsPage = () => {
|
|
|
454
795
|
className: "mt-4",
|
|
455
796
|
asChild: true,
|
|
456
797
|
children: /* @__PURE__ */ jsx(Link, {
|
|
457
|
-
to: "/
|
|
798
|
+
to: "/account/plans",
|
|
458
799
|
children: "View plans"
|
|
459
800
|
})
|
|
460
801
|
})]
|
|
@@ -500,7 +841,7 @@ const SubscriptionsPage = () => {
|
|
|
500
841
|
variant: "outline",
|
|
501
842
|
asChild: true,
|
|
502
843
|
children: /* @__PURE__ */ jsx(Link, {
|
|
503
|
-
to: "/
|
|
844
|
+
to: "/account/plans",
|
|
504
845
|
children: "Change plan"
|
|
505
846
|
})
|
|
506
847
|
}),
|
|
@@ -516,8 +857,8 @@ const SubscriptionsPage = () => {
|
|
|
516
857
|
variant: "secondary",
|
|
517
858
|
asChild: true,
|
|
518
859
|
children: /* @__PURE__ */ jsx(Link, {
|
|
519
|
-
to:
|
|
520
|
-
children: "
|
|
860
|
+
to: CREDENTIALS_PATH,
|
|
861
|
+
children: "API credentials"
|
|
521
862
|
})
|
|
522
863
|
}) : null
|
|
523
864
|
]
|
|
@@ -528,13 +869,11 @@ const SubscriptionsPage = () => {
|
|
|
528
869
|
|
|
529
870
|
//#endregion
|
|
530
871
|
//#region src/DevPortalBillingPlugin.tsx
|
|
531
|
-
const PRICING_PATH = "/pricing";
|
|
532
|
-
const CHECKOUT_PATH = "/checkout";
|
|
533
|
-
const SUBSCRIPTIONS_PATH = "/account/subscriptions";
|
|
534
872
|
const devPortalBillingPlugin = createPlugin((options = {}) => ({
|
|
535
873
|
transformConfig: ({ config, merge }) => merge({ header: { navigation: [...config.header?.navigation ?? [], {
|
|
536
874
|
label: "Pricing",
|
|
537
|
-
to: PRICING_PATH
|
|
875
|
+
to: PRICING_PATH,
|
|
876
|
+
display: "anon"
|
|
538
877
|
}] } }),
|
|
539
878
|
getProfileMenuItems: () => [{
|
|
540
879
|
label: "My Subscriptions",
|
|
@@ -542,6 +881,12 @@ const devPortalBillingPlugin = createPlugin((options = {}) => ({
|
|
|
542
881
|
icon: StarsIcon,
|
|
543
882
|
category: "middle",
|
|
544
883
|
weight: 25
|
|
884
|
+
}, {
|
|
885
|
+
label: "API credentials",
|
|
886
|
+
path: CREDENTIALS_PATH,
|
|
887
|
+
icon: KeyRoundIcon,
|
|
888
|
+
category: "middle",
|
|
889
|
+
weight: 26
|
|
545
890
|
}],
|
|
546
891
|
getRoutes: () => [{
|
|
547
892
|
element: /* @__PURE__ */ jsx(DevPortalBillingWrapper, { options }),
|
|
@@ -561,11 +906,20 @@ const devPortalBillingPlugin = createPlugin((options = {}) => ({
|
|
|
561
906
|
path: SUBSCRIPTIONS_PATH,
|
|
562
907
|
handle: { layout: "default" },
|
|
563
908
|
element: /* @__PURE__ */ jsx(SubscriptionsPage, {})
|
|
909
|
+
},
|
|
910
|
+
{
|
|
911
|
+
path: CREDENTIALS_PATH,
|
|
912
|
+
handle: { layout: "default" },
|
|
913
|
+
element: /* @__PURE__ */ jsx(CredentialsPage, {})
|
|
564
914
|
}
|
|
565
915
|
]
|
|
566
916
|
}],
|
|
567
|
-
getProtectedRoutes: () => [
|
|
917
|
+
getProtectedRoutes: () => [
|
|
918
|
+
CHECKOUT_PATH,
|
|
919
|
+
SUBSCRIPTIONS_PATH,
|
|
920
|
+
CREDENTIALS_PATH
|
|
921
|
+
]
|
|
568
922
|
}));
|
|
569
923
|
|
|
570
924
|
//#endregion
|
|
571
|
-
export { CHECKOUT_PATH, PRICING_PATH, SUBSCRIPTIONS_PATH, devPortalBillingPlugin };
|
|
925
|
+
export { AccountPlansPage, CHECKOUT_PATH, CREDENTIALS_PATH, PRICING_PATH, SUBSCRIPTIONS_PATH, devPortalBillingPlugin };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lukoweb/apitogo-plugin-dev-portal-billing",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.40",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/zuplo/zudoku",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"react": "19.2.4",
|
|
47
47
|
"react-dom": "19.2.4",
|
|
48
48
|
"tsdown": "0.20.3",
|
|
49
|
-
"@lukoweb/apitogo": "0.1.
|
|
49
|
+
"@lukoweb/apitogo": "0.1.40"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"react": ">=19.2.0",
|