@lukoweb/apitogo-plugin-dev-portal-billing 0.1.37 → 0.1.39
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 +5 -2
- package/dist/index.mjs +238 -32
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -8,10 +8,13 @@ type DevPortalBillingConfig = {
|
|
|
8
8
|
};
|
|
9
9
|
};
|
|
10
10
|
//#endregion
|
|
11
|
-
//#region src/
|
|
11
|
+
//#region src/paths.d.ts
|
|
12
12
|
declare const PRICING_PATH = "/pricing";
|
|
13
13
|
declare const CHECKOUT_PATH = "/checkout";
|
|
14
14
|
declare const SUBSCRIPTIONS_PATH = "/account/subscriptions";
|
|
15
|
+
declare const CREDENTIALS_PATH = "/account/credentials";
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/DevPortalBillingPlugin.d.ts
|
|
15
18
|
declare const devPortalBillingPlugin: (options?: DevPortalBillingConfig | undefined) => _lukoweb_apitogo0.ApitogoPlugin;
|
|
16
19
|
//#endregion
|
|
17
|
-
export { CHECKOUT_PATH, PRICING_PATH, SUBSCRIPTIONS_PATH, devPortalBillingPlugin };
|
|
20
|
+
export { CHECKOUT_PATH, CREDENTIALS_PATH, PRICING_PATH, SUBSCRIPTIONS_PATH, devPortalBillingPlugin };
|
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { cn, createPlugin } from "@lukoweb/apitogo";
|
|
2
|
-
import { ShieldIcon, StarsIcon } from "@lukoweb/apitogo/icons";
|
|
2
|
+
import { KeyRoundIcon, ShieldIcon, StarsIcon } from "@lukoweb/apitogo/icons";
|
|
3
3
|
import { Button, ClientOnly, Head, Heading } from "@lukoweb/apitogo/components";
|
|
4
4
|
import { Link, Outlet, useNavigate, useSearchParams } from "@lukoweb/apitogo/router";
|
|
5
|
-
import { createContext, useContext, useEffect } from "react";
|
|
5
|
+
import { createContext, useContext, useEffect, useState } from "react";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { useAuth } from "@lukoweb/apitogo/hooks";
|
|
8
8
|
import { useQuery } from "@lukoweb/apitogo/react-query";
|
|
9
9
|
import { Alert, AlertAction, AlertDescription } from "@lukoweb/apitogo/ui/Alert";
|
|
10
10
|
import { Button as Button$1 } from "@lukoweb/apitogo/ui/Button";
|
|
11
|
+
import { Secret } from "@lukoweb/apitogo/ui/Secret.js";
|
|
11
12
|
|
|
12
13
|
//#region src/DevPortalBillingContext.tsx
|
|
13
14
|
const DevPortalBillingContext = createContext({});
|
|
@@ -248,6 +249,204 @@ const CheckoutPage = () => {
|
|
|
248
249
|
});
|
|
249
250
|
};
|
|
250
251
|
|
|
252
|
+
//#endregion
|
|
253
|
+
//#region src/hooks/useCredentials.ts
|
|
254
|
+
function useCredentials() {
|
|
255
|
+
const auth = useAuth();
|
|
256
|
+
const apiBaseUrl = useDevPortalApiBaseUrl();
|
|
257
|
+
const isPreview = useDevPortalPreviewMode();
|
|
258
|
+
return useQuery({
|
|
259
|
+
queryKey: ["dev-portal-credentials", apiBaseUrl],
|
|
260
|
+
enabled: auth.isAuthenticated && !isPreview && Boolean(apiBaseUrl),
|
|
261
|
+
queryFn: async () => {
|
|
262
|
+
const response = await fetch(`${apiBaseUrl}/api/v1/credentials`, {
|
|
263
|
+
method: "GET",
|
|
264
|
+
headers: { Accept: "application/json" },
|
|
265
|
+
credentials: "include"
|
|
266
|
+
});
|
|
267
|
+
if (response.status === 404) throw new Error("NO_ENTITLEMENT");
|
|
268
|
+
if (!response.ok) throw new Error("Failed to load API credentials.");
|
|
269
|
+
return await response.json();
|
|
270
|
+
},
|
|
271
|
+
retry: (failureCount, error) => error instanceof Error && error.message === "NO_ENTITLEMENT" ? false : failureCount < 2
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/hooks/usePlans.ts
|
|
277
|
+
function usePlans() {
|
|
278
|
+
const apiBaseUrl = useDevPortalApiBaseUrl();
|
|
279
|
+
const isPreview = useDevPortalPreviewMode();
|
|
280
|
+
return useQuery({
|
|
281
|
+
queryKey: [
|
|
282
|
+
"dev-portal-plans",
|
|
283
|
+
apiBaseUrl,
|
|
284
|
+
isPreview
|
|
285
|
+
],
|
|
286
|
+
queryFn: async () => {
|
|
287
|
+
if (isPreview) return parseMockPlans();
|
|
288
|
+
if (!apiBaseUrl) return { plans: [] };
|
|
289
|
+
const response = await fetch(`${apiBaseUrl}/api/v1/plans`, { headers: { Accept: "application/json" } });
|
|
290
|
+
if (!response.ok) throw new Error("Failed to load plans.");
|
|
291
|
+
return await response.json();
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
//#endregion
|
|
297
|
+
//#region src/pages/CredentialsPage.tsx
|
|
298
|
+
const CredentialsPage = () => {
|
|
299
|
+
const auth = useAuth();
|
|
300
|
+
const isPreview = useDevPortalPreviewMode();
|
|
301
|
+
const { data: credentials, isLoading, error } = useCredentials();
|
|
302
|
+
const { data: catalog } = usePlans();
|
|
303
|
+
const [revealed, setRevealed] = useState(false);
|
|
304
|
+
const plan = catalog?.plans.find((item) => item.sku === credentials?.planSku);
|
|
305
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
306
|
+
className: "mx-auto max-w-screen-lg px-4 pb-16 pt-(--padding-content-top) lg:px-8",
|
|
307
|
+
children: [
|
|
308
|
+
/* @__PURE__ */ jsx(Head, { children: /* @__PURE__ */ jsx("title", { children: "API credentials" }) }),
|
|
309
|
+
/* @__PURE__ */ jsxs("div", {
|
|
310
|
+
className: "mb-8 flex items-start gap-4",
|
|
311
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
312
|
+
className: "flex size-12 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary",
|
|
313
|
+
children: /* @__PURE__ */ jsx(KeyRoundIcon, { size: 24 })
|
|
314
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
315
|
+
className: "space-y-1",
|
|
316
|
+
children: [/* @__PURE__ */ jsx("h1", {
|
|
317
|
+
className: "text-3xl font-semibold tracking-tight",
|
|
318
|
+
children: "API credentials"
|
|
319
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
320
|
+
className: "text-muted-foreground",
|
|
321
|
+
children: "Use your subscription key to authenticate requests to the API gateway."
|
|
322
|
+
})]
|
|
323
|
+
})]
|
|
324
|
+
}),
|
|
325
|
+
isPreview ? /* @__PURE__ */ jsx("div", {
|
|
326
|
+
className: "rounded-xl border border-dashed p-8 text-center",
|
|
327
|
+
children: /* @__PURE__ */ jsx("p", {
|
|
328
|
+
className: "text-muted-foreground",
|
|
329
|
+
children: "API credentials are available after the developer portal is published."
|
|
330
|
+
})
|
|
331
|
+
}) : null,
|
|
332
|
+
!isPreview && !auth.isAuthenticated ? /* @__PURE__ */ jsxs("div", {
|
|
333
|
+
className: "rounded-xl border border-dashed p-8 text-center",
|
|
334
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
335
|
+
className: "text-muted-foreground",
|
|
336
|
+
children: "Sign in to view your subscription key."
|
|
337
|
+
}), /* @__PURE__ */ jsx(Button$1, {
|
|
338
|
+
className: "mt-4",
|
|
339
|
+
asChild: true,
|
|
340
|
+
children: /* @__PURE__ */ jsx(Link, {
|
|
341
|
+
to: "/pricing",
|
|
342
|
+
children: "View plans"
|
|
343
|
+
})
|
|
344
|
+
})]
|
|
345
|
+
}) : null,
|
|
346
|
+
!isPreview && auth.isAuthenticated && isLoading ? /* @__PURE__ */ jsx("p", {
|
|
347
|
+
className: "text-muted-foreground",
|
|
348
|
+
children: "Loading credentials…"
|
|
349
|
+
}) : null,
|
|
350
|
+
!isPreview && auth.isAuthenticated && error ? /* @__PURE__ */ jsxs("div", {
|
|
351
|
+
className: "rounded-xl border border-dashed p-8 text-center",
|
|
352
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
353
|
+
className: "text-muted-foreground",
|
|
354
|
+
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."
|
|
355
|
+
}), /* @__PURE__ */ jsx(Button$1, {
|
|
356
|
+
className: "mt-4",
|
|
357
|
+
asChild: true,
|
|
358
|
+
children: /* @__PURE__ */ jsx(Link, {
|
|
359
|
+
to: "/pricing",
|
|
360
|
+
children: "View plans"
|
|
361
|
+
})
|
|
362
|
+
})]
|
|
363
|
+
}) : null,
|
|
364
|
+
!isPreview && credentials ? /* @__PURE__ */ jsxs("div", {
|
|
365
|
+
className: "space-y-6",
|
|
366
|
+
children: [
|
|
367
|
+
/* @__PURE__ */ jsx("article", {
|
|
368
|
+
className: "rounded-xl border bg-card/60 p-6",
|
|
369
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
370
|
+
className: "space-y-1",
|
|
371
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
372
|
+
className: "text-lg font-medium",
|
|
373
|
+
children: "Active plan"
|
|
374
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
375
|
+
className: "text-sm text-muted-foreground",
|
|
376
|
+
children: plan?.displayName ?? credentials.planSku
|
|
377
|
+
})]
|
|
378
|
+
})
|
|
379
|
+
}),
|
|
380
|
+
/* @__PURE__ */ jsxs("article", {
|
|
381
|
+
className: "rounded-xl border bg-card/60 p-6 space-y-4",
|
|
382
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
383
|
+
className: "space-y-1",
|
|
384
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
385
|
+
className: "text-lg font-medium",
|
|
386
|
+
children: "Subscription key"
|
|
387
|
+
}), /* @__PURE__ */ jsxs("p", {
|
|
388
|
+
className: "text-sm text-muted-foreground",
|
|
389
|
+
children: [
|
|
390
|
+
"Send this value in the",
|
|
391
|
+
" ",
|
|
392
|
+
/* @__PURE__ */ jsx("code", {
|
|
393
|
+
className: "rounded bg-muted px-1 py-0.5 text-xs",
|
|
394
|
+
children: "Ocp-Apim-Subscription-Key"
|
|
395
|
+
}),
|
|
396
|
+
" ",
|
|
397
|
+
"header when calling the gateway."
|
|
398
|
+
]
|
|
399
|
+
})]
|
|
400
|
+
}), /* @__PURE__ */ jsx(Secret, {
|
|
401
|
+
className: "max-w-full w-full",
|
|
402
|
+
secret: credentials.subscriptionKey,
|
|
403
|
+
status: "active",
|
|
404
|
+
revealed,
|
|
405
|
+
onReveal: setRevealed
|
|
406
|
+
})]
|
|
407
|
+
}),
|
|
408
|
+
/* @__PURE__ */ jsxs("article", {
|
|
409
|
+
className: "rounded-xl border bg-card/60 p-6 space-y-4",
|
|
410
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
411
|
+
className: "space-y-1",
|
|
412
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
413
|
+
className: "text-lg font-medium",
|
|
414
|
+
children: "Gateway URL"
|
|
415
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
416
|
+
className: "text-sm text-muted-foreground",
|
|
417
|
+
children: "Base URL for API requests through the gateway."
|
|
418
|
+
})]
|
|
419
|
+
}), /* @__PURE__ */ jsx(Secret, {
|
|
420
|
+
className: "max-w-full w-full",
|
|
421
|
+
secret: credentials.gatewayBaseUrl,
|
|
422
|
+
status: "none",
|
|
423
|
+
noReveal: true
|
|
424
|
+
})]
|
|
425
|
+
})
|
|
426
|
+
]
|
|
427
|
+
}) : null,
|
|
428
|
+
/* @__PURE__ */ jsxs("div", {
|
|
429
|
+
className: "mt-8 flex flex-wrap gap-3",
|
|
430
|
+
children: [/* @__PURE__ */ jsx(Button$1, {
|
|
431
|
+
variant: "outline",
|
|
432
|
+
asChild: true,
|
|
433
|
+
children: /* @__PURE__ */ jsx(Link, {
|
|
434
|
+
to: "/account/subscriptions",
|
|
435
|
+
children: "My subscriptions"
|
|
436
|
+
})
|
|
437
|
+
}), /* @__PURE__ */ jsx(Button$1, {
|
|
438
|
+
variant: "outline",
|
|
439
|
+
asChild: true,
|
|
440
|
+
children: /* @__PURE__ */ jsx(Link, {
|
|
441
|
+
to: "/api",
|
|
442
|
+
children: "API reference"
|
|
443
|
+
})
|
|
444
|
+
})]
|
|
445
|
+
})
|
|
446
|
+
]
|
|
447
|
+
});
|
|
448
|
+
};
|
|
449
|
+
|
|
251
450
|
//#endregion
|
|
252
451
|
//#region src/components/PricingCard.tsx
|
|
253
452
|
const PricingCard = ({ plan, isSubscribed = false }) => {
|
|
@@ -317,27 +516,6 @@ function useMySubscriptions() {
|
|
|
317
516
|
});
|
|
318
517
|
}
|
|
319
518
|
|
|
320
|
-
//#endregion
|
|
321
|
-
//#region src/hooks/usePlans.ts
|
|
322
|
-
function usePlans() {
|
|
323
|
-
const apiBaseUrl = useDevPortalApiBaseUrl();
|
|
324
|
-
const isPreview = useDevPortalPreviewMode();
|
|
325
|
-
return useQuery({
|
|
326
|
-
queryKey: [
|
|
327
|
-
"dev-portal-plans",
|
|
328
|
-
apiBaseUrl,
|
|
329
|
-
isPreview
|
|
330
|
-
],
|
|
331
|
-
queryFn: async () => {
|
|
332
|
-
if (isPreview) return parseMockPlans();
|
|
333
|
-
if (!apiBaseUrl) return { plans: [] };
|
|
334
|
-
const response = await fetch(`${apiBaseUrl}/api/v1/plans`, { headers: { Accept: "application/json" } });
|
|
335
|
-
if (!response.ok) throw new Error("Failed to load plans.");
|
|
336
|
-
return await response.json();
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
|
|
341
519
|
//#endregion
|
|
342
520
|
//#region src/pages/PricingPage.tsx
|
|
343
521
|
const PricingPage = () => {
|
|
@@ -413,6 +591,13 @@ const PricingPage = () => {
|
|
|
413
591
|
});
|
|
414
592
|
};
|
|
415
593
|
|
|
594
|
+
//#endregion
|
|
595
|
+
//#region src/paths.ts
|
|
596
|
+
const PRICING_PATH = "/pricing";
|
|
597
|
+
const CHECKOUT_PATH = "/checkout";
|
|
598
|
+
const SUBSCRIPTIONS_PATH = "/account/subscriptions";
|
|
599
|
+
const CREDENTIALS_PATH = "/account/credentials";
|
|
600
|
+
|
|
416
601
|
//#endregion
|
|
417
602
|
//#region src/pages/SubscriptionsPage.tsx
|
|
418
603
|
const SubscriptionsPage = () => {
|
|
@@ -437,9 +622,18 @@ const SubscriptionsPage = () => {
|
|
|
437
622
|
children: "Review your active plans and manage billing."
|
|
438
623
|
})]
|
|
439
624
|
}),
|
|
440
|
-
checkoutSuccess ? /* @__PURE__ */
|
|
625
|
+
checkoutSuccess ? /* @__PURE__ */ jsxs("div", {
|
|
441
626
|
className: "mb-6 rounded-xl border border-green-500/30 bg-green-500/10 p-4 text-sm",
|
|
442
|
-
children:
|
|
627
|
+
children: [
|
|
628
|
+
"Your subscription is active.",
|
|
629
|
+
" ",
|
|
630
|
+
/* @__PURE__ */ jsx(Link, {
|
|
631
|
+
to: CREDENTIALS_PATH,
|
|
632
|
+
className: "font-medium underline",
|
|
633
|
+
children: "View your API credentials"
|
|
634
|
+
}),
|
|
635
|
+
"."
|
|
636
|
+
]
|
|
443
637
|
}) : null,
|
|
444
638
|
isLoading ? /* @__PURE__ */ jsx("p", {
|
|
445
639
|
className: "text-muted-foreground",
|
|
@@ -516,8 +710,8 @@ const SubscriptionsPage = () => {
|
|
|
516
710
|
variant: "secondary",
|
|
517
711
|
asChild: true,
|
|
518
712
|
children: /* @__PURE__ */ jsx(Link, {
|
|
519
|
-
to:
|
|
520
|
-
children: "
|
|
713
|
+
to: CREDENTIALS_PATH,
|
|
714
|
+
children: "API credentials"
|
|
521
715
|
})
|
|
522
716
|
}) : null
|
|
523
717
|
]
|
|
@@ -528,9 +722,6 @@ const SubscriptionsPage = () => {
|
|
|
528
722
|
|
|
529
723
|
//#endregion
|
|
530
724
|
//#region src/DevPortalBillingPlugin.tsx
|
|
531
|
-
const PRICING_PATH = "/pricing";
|
|
532
|
-
const CHECKOUT_PATH = "/checkout";
|
|
533
|
-
const SUBSCRIPTIONS_PATH = "/account/subscriptions";
|
|
534
725
|
const devPortalBillingPlugin = createPlugin((options = {}) => ({
|
|
535
726
|
transformConfig: ({ config, merge }) => merge({ header: { navigation: [...config.header?.navigation ?? [], {
|
|
536
727
|
label: "Pricing",
|
|
@@ -542,6 +733,12 @@ const devPortalBillingPlugin = createPlugin((options = {}) => ({
|
|
|
542
733
|
icon: StarsIcon,
|
|
543
734
|
category: "middle",
|
|
544
735
|
weight: 25
|
|
736
|
+
}, {
|
|
737
|
+
label: "API credentials",
|
|
738
|
+
path: CREDENTIALS_PATH,
|
|
739
|
+
icon: KeyRoundIcon,
|
|
740
|
+
category: "middle",
|
|
741
|
+
weight: 26
|
|
545
742
|
}],
|
|
546
743
|
getRoutes: () => [{
|
|
547
744
|
element: /* @__PURE__ */ jsx(DevPortalBillingWrapper, { options }),
|
|
@@ -561,11 +758,20 @@ const devPortalBillingPlugin = createPlugin((options = {}) => ({
|
|
|
561
758
|
path: SUBSCRIPTIONS_PATH,
|
|
562
759
|
handle: { layout: "default" },
|
|
563
760
|
element: /* @__PURE__ */ jsx(SubscriptionsPage, {})
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
path: CREDENTIALS_PATH,
|
|
764
|
+
handle: { layout: "default" },
|
|
765
|
+
element: /* @__PURE__ */ jsx(CredentialsPage, {})
|
|
564
766
|
}
|
|
565
767
|
]
|
|
566
768
|
}],
|
|
567
|
-
getProtectedRoutes: () => [
|
|
769
|
+
getProtectedRoutes: () => [
|
|
770
|
+
CHECKOUT_PATH,
|
|
771
|
+
SUBSCRIPTIONS_PATH,
|
|
772
|
+
CREDENTIALS_PATH
|
|
773
|
+
]
|
|
568
774
|
}));
|
|
569
775
|
|
|
570
776
|
//#endregion
|
|
571
|
-
export { CHECKOUT_PATH, PRICING_PATH, SUBSCRIPTIONS_PATH, devPortalBillingPlugin };
|
|
777
|
+
export { 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.39",
|
|
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.39"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"react": ">=19.2.0",
|