@lukoweb/apitogo-plugin-dev-portal-billing 0.2.30 → 0.2.34

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.
@@ -0,0 +1,12 @@
1
+ //#region src/paths.ts
2
+ const PRICING_PATH = "/pricing";
3
+ const ACCOUNT_PLANS_PATH = "/account/plans";
4
+ const CHECKOUT_PATH = "/checkout";
5
+ const CONTACT_SALES_PATH = "/contact-sales";
6
+ const SUBSCRIPTIONS_PATH = "/account/subscriptions";
7
+ const CREDENTIALS_PATH = "/account/credentials";
8
+ const USAGE_PATH = "/account/usage";
9
+ const LOGIN_NAV_PATH = "/signin";
10
+
11
+ //#endregion
12
+ export { LOGIN_NAV_PATH as a, USAGE_PATH as c, CREDENTIALS_PATH as i, CHECKOUT_PATH as n, PRICING_PATH as o, CONTACT_SALES_PATH as r, SUBSCRIPTIONS_PATH as s, ACCOUNT_PLANS_PATH as t };
@@ -0,0 +1,11 @@
1
+ import manifestCatalog from "virtual:apitogo-local-manifest";
2
+ import zudokuConfig from "virtual:zudoku-config";
3
+
4
+ //#region src/utils/pricing-config.ts
5
+ function isPreviewPricingEnabled() {
6
+ if (manifestCatalog?.pricingEnabled === true) return true;
7
+ return zudokuConfig?.__meta?.pricingEnabled === true;
8
+ }
9
+
10
+ //#endregion
11
+ export { isPreviewPricingEnabled as t };
@@ -0,0 +1,241 @@
1
+ import manifestCatalog from "virtual:apitogo-local-manifest";
2
+ import zudokuConfig from "virtual:zudoku-config";
3
+ import { useAuth } from "@lukoweb/apitogo/hooks";
4
+
5
+ //#region src/utils/dev-portal-api.ts
6
+ const DEV_PORTAL_PLACEHOLDER_API_URL = "https://dp-example.azurecontainerapps.io";
7
+ function resolveDevPortalApiBaseUrl(configuredUrl) {
8
+ const fromConfig = configuredUrl?.trim();
9
+ if (fromConfig) return fromConfig.replace(/\/+$/, "");
10
+ const envUrl = (typeof VITE_APITOGO_AUTHENTICATION_API_BASE_URL === "string" ? VITE_APITOGO_AUTHENTICATION_API_BASE_URL.trim() : "") || (typeof VITE_APITOGO_DEV_PORTAL_API_URL === "string" ? VITE_APITOGO_DEV_PORTAL_API_URL.trim() : "");
11
+ return envUrl ? envUrl.replace(/\/+$/, "") : void 0;
12
+ }
13
+ function isPlaceholderDevPortalApiUrl(apiBaseUrl) {
14
+ if (!apiBaseUrl) return true;
15
+ return apiBaseUrl === DEV_PORTAL_PLACEHOLDER_API_URL || apiBaseUrl.includes("dp-example.azurecontainerapps.io");
16
+ }
17
+ function resolveLimitsJson(plan) {
18
+ if (plan.limits && typeof plan.limits === "object" && !Array.isArray(plan.limits)) return JSON.stringify(plan.limits);
19
+ return plan.limitsJson ?? null;
20
+ }
21
+ function mapPreviewPlans(plans) {
22
+ return plans.map((plan) => ({
23
+ sku: plan.sku,
24
+ displayName: plan.displayName,
25
+ priceAmount: plan.priceAmount,
26
+ pricingModel: plan.pricingModel,
27
+ priceLabel: plan.priceLabel,
28
+ priceCurrency: plan.priceCurrency,
29
+ billingPeriod: plan.billingPeriod,
30
+ limitsJson: resolveLimitsJson(plan),
31
+ includedActionKeys: plan.includedActionKeys,
32
+ features: plan.features
33
+ }));
34
+ }
35
+ function readModulePlanItems() {
36
+ const items = (zudokuConfig?.modules)?.userPanel?.plans?.items;
37
+ return Array.isArray(items) && items.length > 0 ? items : void 0;
38
+ }
39
+ function parseManifestPlans(configPlans) {
40
+ if (configPlans?.length) return { plans: mapPreviewPlans(configPlans.map((plan) => ({
41
+ sku: plan.sku,
42
+ displayName: plan.displayName,
43
+ priceAmount: plan.priceAmount ?? 0,
44
+ pricingModel: plan.pricingModel,
45
+ priceLabel: plan.priceLabel,
46
+ priceCurrency: plan.priceCurrency ?? "usd",
47
+ billingPeriod: plan.billingPeriod ?? "monthly",
48
+ limits: plan.limits,
49
+ limitsJson: plan.limitsJson ?? null,
50
+ includedActionKeys: plan.includedActionKeys ?? [],
51
+ features: plan.features
52
+ }))) };
53
+ if (manifestCatalog?.plans?.length) return { plans: mapPreviewPlans(manifestCatalog.plans) };
54
+ const metaPlans = zudokuConfig?.__meta?.previewPlans;
55
+ if (metaPlans?.length) return { plans: mapPreviewPlans(metaPlans) };
56
+ const moduleItems = readModulePlanItems();
57
+ if (moduleItems?.length) return { plans: mapPreviewPlans(moduleItems.map((plan) => ({
58
+ sku: plan.sku,
59
+ displayName: plan.displayName,
60
+ priceAmount: plan.priceAmount ?? 0,
61
+ pricingModel: plan.pricingModel,
62
+ priceLabel: plan.priceLabel,
63
+ priceCurrency: plan.priceCurrency ?? "usd",
64
+ billingPeriod: plan.billingPeriod ?? "monthly",
65
+ limits: plan.limits,
66
+ limitsJson: plan.limitsJson ?? null,
67
+ includedActionKeys: plan.includedActionKeys ?? [],
68
+ features: plan.features
69
+ }))) };
70
+ return { plans: [] };
71
+ }
72
+ function formatActionKey(actionKey) {
73
+ const separatorIndex = actionKey.indexOf(":");
74
+ if (separatorIndex <= 0) return actionKey;
75
+ return `${actionKey.slice(0, separatorIndex).toUpperCase()} ${actionKey.slice(separatorIndex + 1)}`;
76
+ }
77
+ /** Custom-priced plans go to a contact form; a negative price is the legacy marker for the same. */
78
+ function isPlanCustomPriced(plan) {
79
+ return plan.pricingModel?.toLowerCase() === "custom" || plan.priceAmount < 0;
80
+ }
81
+ function formatCustomPriceLabel(plan) {
82
+ return plan.priceLabel?.trim() || "Custom";
83
+ }
84
+ function formatPlanPrice(plan) {
85
+ if (isPlanCustomPriced(plan)) return formatCustomPriceLabel(plan);
86
+ if (plan.priceAmount <= 0) return "Free";
87
+ const amount = new Intl.NumberFormat(void 0, {
88
+ style: "currency",
89
+ currency: plan.priceCurrency.toUpperCase(),
90
+ minimumFractionDigits: plan.priceAmount % 1 === 0 ? 0 : 2
91
+ }).format(plan.priceAmount);
92
+ const period = plan.billingPeriod.toLowerCase();
93
+ if (period === "monthly" || period === "month") return `${amount}/mo`;
94
+ if (period === "annual" || period === "year") return `${amount}/yr`;
95
+ return amount;
96
+ }
97
+ function isPlanFree(plan) {
98
+ return !isPlanCustomPriced(plan) && plan.priceAmount <= 0;
99
+ }
100
+ function formatBillingPeriod(period) {
101
+ const normalized = period.toLowerCase();
102
+ if (normalized === "month" || normalized === "monthly") return "per month";
103
+ if (normalized === "year" || normalized === "annual") return "per year";
104
+ if (normalized === "custom") return "talk to us";
105
+ return period;
106
+ }
107
+ /** Headline price for a plan card, without the period suffix (e.g. `$19`, `$0`, `Custom`). */
108
+ function formatPlanDisplayPrice(plan) {
109
+ if (isPlanCustomPriced(plan)) return formatCustomPriceLabel(plan);
110
+ if (isPlanFree(plan)) return "$0";
111
+ const priceLabel = formatPlanPrice(plan);
112
+ return priceLabel.replace(/\/mo$/, "").replace(/\/yr$/, "") || priceLabel;
113
+ }
114
+ /** Caption shown under the plan price (e.g. `per month`, `talk to us`). */
115
+ function formatPlanPriceUnit(plan) {
116
+ return isPlanCustomPriced(plan) ? "talk to us" : formatBillingPeriod(plan.billingPeriod);
117
+ }
118
+ /** SKU of the plan to highlight: `developer` if present, else the first low-priced paid plan. */
119
+ function pickFeaturedPlanSku(plans) {
120
+ return plans.find((p) => p.sku === "developer")?.sku ?? plans.find((p) => p.priceAmount > 0 && p.priceAmount < 50)?.sku ?? plans.find((p) => p.priceAmount > 0)?.sku;
121
+ }
122
+ function formatRateLimit(rate) {
123
+ if (!rate || typeof rate !== "object") return null;
124
+ const { calls, renewalPeriodSeconds } = rate;
125
+ if (calls == null || renewalPeriodSeconds == null) return null;
126
+ if (renewalPeriodSeconds === 1) return `${calls} req/sec rate limit`;
127
+ const window = renewalPeriodSeconds === 60 ? "minute" : `${renewalPeriodSeconds} seconds`;
128
+ return `${calls} request${calls === 1 ? "" : "s"} per ${window}`;
129
+ }
130
+ function formatQuota(quota) {
131
+ if (!quota || typeof quota !== "object") return null;
132
+ const { calls, renewalPeriodDays } = quota;
133
+ if (calls == null) return null;
134
+ if (calls < 0) return "Unlimited requests";
135
+ const formatted = calls.toLocaleString();
136
+ if (renewalPeriodDays === 30 || renewalPeriodDays === 31) return `${formatted} requests / month`;
137
+ if (renewalPeriodDays === 1) return `${formatted} requests / day`;
138
+ if (renewalPeriodDays != null) return `${formatted} requests / ${renewalPeriodDays} days`;
139
+ return `${formatted} requests`;
140
+ }
141
+ /** The quota of a plan as a short table cell ("1,000 / month"), or null when the plan has none. */
142
+ function quotaCell(limitsJson) {
143
+ const quota = parseLimits(limitsJson)?.quota;
144
+ if (!quota || typeof quota !== "object") return null;
145
+ const { calls, renewalPeriodDays } = quota;
146
+ if (calls == null) return null;
147
+ if (calls < 0) return "Unlimited";
148
+ const formatted = calls.toLocaleString("en-US");
149
+ if (renewalPeriodDays === 30 || renewalPeriodDays === 31) return `${formatted} / month`;
150
+ if (renewalPeriodDays === 1) return `${formatted} / day`;
151
+ if (renewalPeriodDays != null) return `${formatted} / ${renewalPeriodDays} days`;
152
+ return formatted;
153
+ }
154
+ /** The rate limit of a plan as a short table cell ("3 / min"), or null when the plan has none. */
155
+ function rateLimitCell(limitsJson) {
156
+ const rate = parseLimits(limitsJson)?.rateLimit;
157
+ if (!rate || typeof rate !== "object") return null;
158
+ const { calls, renewalPeriodSeconds } = rate;
159
+ if (calls == null || renewalPeriodSeconds == null) return null;
160
+ const formatted = calls.toLocaleString("en-US");
161
+ if (renewalPeriodSeconds === 1) return `${formatted} / sec`;
162
+ if (renewalPeriodSeconds === 60) return `${formatted} / min`;
163
+ if (renewalPeriodSeconds === 3600) return `${formatted} / hour`;
164
+ return `${formatted} / ${renewalPeriodSeconds} sec`;
165
+ }
166
+ function parseLimits(limitsJson) {
167
+ if (!limitsJson?.trim()) return null;
168
+ try {
169
+ return JSON.parse(limitsJson);
170
+ } catch {
171
+ return null;
172
+ }
173
+ }
174
+ /** Marketing bullets keyed by common sample SKUs when limitsJson is sparse. */
175
+ const SAMPLE_PLAN_FEATURE_EXTRAS = {
176
+ free: ["All endpoints"],
177
+ developer: ["Webhooks included", "Email support"],
178
+ team: ["5 team seats", "Priority support"],
179
+ enterprise: [
180
+ "Unlimited requests",
181
+ "Custom rate limits",
182
+ "SSO & audit logs",
183
+ "Dedicated SLA"
184
+ ]
185
+ };
186
+ function listPlanFeatureLines(plan) {
187
+ if (plan.features && plan.features.length > 0) return plan.features;
188
+ const lines = [];
189
+ if (plan.limitsJson?.trim()) try {
190
+ const limits = JSON.parse(plan.limitsJson);
191
+ const quotaLine = formatQuota(limits.quota);
192
+ if (quotaLine) lines.push(quotaLine);
193
+ const rateLine = formatRateLimit(limits.rateLimit);
194
+ if (rateLine) lines.push(rateLine);
195
+ } catch {}
196
+ const extras = SAMPLE_PLAN_FEATURE_EXTRAS[plan.sku.toLowerCase()];
197
+ if (extras) {
198
+ for (const extra of extras) if (!lines.includes(extra)) lines.push(extra);
199
+ }
200
+ if (plan.includedActionKeys && plan.includedActionKeys.length > 0) for (const actionKey of plan.includedActionKeys) lines.push(formatActionKey(actionKey));
201
+ if (lines.length === 0 && isPlanFree(plan)) lines.push("API access not included");
202
+ return lines;
203
+ }
204
+ function isActiveSubscription(status) {
205
+ return status.toLowerCase() === "active";
206
+ }
207
+ /** Returns monthly call quota from plan limitsJson, or null when unknown/unlimited. */
208
+ function parseMonthlyQuotaCalls(limitsJson) {
209
+ if (!limitsJson?.trim()) return null;
210
+ try {
211
+ const quota = JSON.parse(limitsJson).quota;
212
+ if (!quota || typeof quota !== "object") return null;
213
+ const { calls, renewalPeriodDays } = quota;
214
+ if (calls == null || calls < 0) return null;
215
+ if (renewalPeriodDays == null || renewalPeriodDays === 30 || renewalPeriodDays === 31) return calls;
216
+ return null;
217
+ } catch {
218
+ return null;
219
+ }
220
+ }
221
+
222
+ //#endregion
223
+ //#region src/hooks/useDevPortalApiBaseUrl.ts
224
+ function useDevPortalApiBaseUrl() {
225
+ const providerData = useAuth().providerData;
226
+ if (providerData?.type === "dev-portal" && providerData.apiBaseUrl) return providerData.apiBaseUrl;
227
+ return resolveDevPortalApiBaseUrl();
228
+ }
229
+ function useIsDemoSession() {
230
+ return useAuth().isDemoSession === true;
231
+ }
232
+ function useDevPortalPreviewMode() {
233
+ const auth = useAuth();
234
+ const apiBaseUrl = useDevPortalApiBaseUrl();
235
+ if (auth.isDemoSession) return false;
236
+ if (auth.authMode === "preview" || auth.isBackendAvailable === false) return true;
237
+ return isPlaceholderDevPortalApiUrl(apiBaseUrl);
238
+ }
239
+
240
+ //#endregion
241
+ export { rateLimitCell as _, formatPlanDisplayPrice as a, isActiveSubscription as c, isPlanFree as d, listPlanFeatureLines as f, quotaCell as g, pickFeaturedPlanSku as h, formatBillingPeriod as i, isPlaceholderDevPortalApiUrl as l, parseMonthlyQuotaCalls as m, useDevPortalPreviewMode as n, formatPlanPrice as o, parseManifestPlans as p, useIsDemoSession as r, formatPlanPriceUnit as s, useDevPortalApiBaseUrl as t, isPlanCustomPriced as u, resolveDevPortalApiBaseUrl as v };
@@ -0,0 +1,42 @@
1
+ import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-CgygF2z5.mjs";
2
+ import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
3
+ import { r as createDemoMeResponse } from "./demo-session-Cez740bi.mjs";
4
+ import { useQuery } from "@lukoweb/apitogo/react-query";
5
+ import { useAuth } from "@lukoweb/apitogo/hooks";
6
+
7
+ //#region src/hooks/useMySubscriptions.ts
8
+ function useMySubscriptions() {
9
+ const auth = useAuth();
10
+ const apiBaseUrl = useDevPortalApiBaseUrl();
11
+ const isPreview = useDevPortalPreviewMode();
12
+ const isDemoSession = useIsDemoSession();
13
+ const { previewPlans } = useDevPortalBillingConfig();
14
+ const liveQuery = useQuery({
15
+ queryKey: ["dev-portal-me", apiBaseUrl],
16
+ enabled: auth.isAuthenticated && !isPreview && !isDemoSession && Boolean(apiBaseUrl),
17
+ queryFn: async () => {
18
+ const response = await fetch(`${apiBaseUrl}/api/v1/auth/me`, {
19
+ method: "GET",
20
+ headers: { Accept: "application/json" },
21
+ credentials: "include"
22
+ });
23
+ if (!response.ok) throw new Error("Failed to load subscription status.");
24
+ return await response.json();
25
+ }
26
+ });
27
+ if (isDemoSession && auth.isAuthenticated) return {
28
+ ...liveQuery,
29
+ data: createDemoMeResponse(previewPlans),
30
+ error: null,
31
+ isError: false,
32
+ isFetching: false,
33
+ isLoading: false,
34
+ isPending: false,
35
+ isSuccess: true,
36
+ status: "success"
37
+ };
38
+ return liveQuery;
39
+ }
40
+
41
+ //#endregion
42
+ export { useMySubscriptions as t };
@@ -0,0 +1,69 @@
1
+ import { l as isPlaceholderDevPortalApiUrl, n as useDevPortalPreviewMode, p as parseManifestPlans, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-CgygF2z5.mjs";
2
+ import { n as useDevPortalBillingConfig } from "./DevPortalBillingContext-CR83s4LO.mjs";
3
+ import { useQuery } from "@lukoweb/apitogo/react-query";
4
+
5
+ //#region src/utils/plans-prefetch.ts
6
+ async function requestPlans(apiBaseUrl) {
7
+ const response = await fetch(`${apiBaseUrl}/api/v1/plans`, { headers: { Accept: "application/json" } });
8
+ if (!response.ok) throw new Error("Failed to load plans.");
9
+ return await response.json();
10
+ }
11
+ let prefetched = null;
12
+ /**
13
+ * Starts the public plans request before the page has hydrated, so it runs next to the app code loading instead of
14
+ * after it. The first query for the same address takes the answer; a failed early request is simply asked again.
15
+ */
16
+ function startPlansPrefetch(apiBaseUrl) {
17
+ if (typeof window === "undefined" || prefetched || !apiBaseUrl || isPlaceholderDevPortalApiUrl(apiBaseUrl)) return;
18
+ const promise = requestPlans(apiBaseUrl);
19
+ promise.catch(() => void 0);
20
+ prefetched = {
21
+ apiBaseUrl,
22
+ promise
23
+ };
24
+ }
25
+ async function loadPlans(apiBaseUrl) {
26
+ const early = prefetched;
27
+ prefetched = null;
28
+ if (early?.apiBaseUrl === apiBaseUrl) try {
29
+ return await early.promise;
30
+ } catch {}
31
+ return requestPlans(apiBaseUrl);
32
+ }
33
+
34
+ //#endregion
35
+ //#region src/hooks/usePlans.ts
36
+ function usePlans() {
37
+ const apiBaseUrl = useDevPortalApiBaseUrl();
38
+ const isPreview = useDevPortalPreviewMode();
39
+ const isDemoSession = useIsDemoSession();
40
+ const { previewPlans } = useDevPortalBillingConfig();
41
+ const liveQuery = useQuery({
42
+ queryKey: ["dev-portal-plans", apiBaseUrl],
43
+ enabled: !isPreview && !isDemoSession && Boolean(apiBaseUrl),
44
+ queryFn: async () => {
45
+ if (!apiBaseUrl) return { plans: [] };
46
+ return loadPlans(apiBaseUrl);
47
+ }
48
+ });
49
+ if (isPreview || isDemoSession) {
50
+ const data = parseManifestPlans(previewPlans);
51
+ return {
52
+ ...liveQuery,
53
+ data,
54
+ dataUpdatedAt: Date.now(),
55
+ error: null,
56
+ fetchStatus: "idle",
57
+ isError: false,
58
+ isFetching: false,
59
+ isLoading: false,
60
+ isPending: false,
61
+ isSuccess: true,
62
+ status: "success"
63
+ };
64
+ }
65
+ return liveQuery;
66
+ }
67
+
68
+ //#endregion
69
+ export { startPlansPrefetch as n, usePlans as t };
@@ -0,0 +1,164 @@
1
+ import { n as useDevPortalPreviewMode, r as useIsDemoSession, t as useDevPortalApiBaseUrl } from "./useDevPortalApiBaseUrl-CgygF2z5.mjs";
2
+ import { useQuery } from "@lukoweb/apitogo/react-query";
3
+ import { useAuth } from "@lukoweb/apitogo/hooks";
4
+
5
+ //#region src/hooks/useUsage.ts
6
+ function createDemoUsage(range) {
7
+ const now = /* @__PURE__ */ new Date();
8
+ const days = range === "24h" ? 1 : range === "7d" ? 7 : range === "90d" ? 90 : range === "mtd" ? Math.max(1, now.getUTCDate()) : 30;
9
+ const series = [];
10
+ let total = 0;
11
+ const today = /* @__PURE__ */ new Date();
12
+ for (let i = days - 1; i >= 0; i--) {
13
+ const day = new Date(today);
14
+ day.setUTCDate(day.getUTCDate() - i);
15
+ const requestCount = 40 + i * 17 % 80;
16
+ total += requestCount;
17
+ series.push({
18
+ dayUtc: day.toISOString().slice(0, 10),
19
+ requestCount,
20
+ errorCount: Math.floor(requestCount * .02),
21
+ avgLatencyMs: 40 + i % 20
22
+ });
23
+ }
24
+ return {
25
+ range,
26
+ fromDayUtc: series[0]?.dayUtc ?? today.toISOString().slice(0, 10),
27
+ toDayUtc: series.at(-1)?.dayUtc ?? today.toISOString().slice(0, 10),
28
+ requestCount: total,
29
+ avgLatencyMs: 48,
30
+ errorRate: .018,
31
+ series,
32
+ byEndpoint: [
33
+ {
34
+ path: "GET /pets",
35
+ count: Math.floor(total * .45)
36
+ },
37
+ {
38
+ path: "POST /pets",
39
+ count: Math.floor(total * .25)
40
+ },
41
+ {
42
+ path: "GET /pets/{id}",
43
+ count: Math.floor(total * .2)
44
+ },
45
+ {
46
+ path: "DELETE /pets/{id}",
47
+ count: Math.floor(total * .1)
48
+ }
49
+ ],
50
+ byStatus: [
51
+ {
52
+ label: "2xx",
53
+ count: Math.floor(total * .96)
54
+ },
55
+ {
56
+ label: "4xx",
57
+ count: Math.floor(total * .03)
58
+ },
59
+ {
60
+ label: "5xx",
61
+ count: Math.floor(total * .01)
62
+ }
63
+ ]
64
+ };
65
+ }
66
+ function createDemoRecent() {
67
+ const now = Date.now();
68
+ return { items: [
69
+ {
70
+ occurredAtUtc: (/* @__PURE__ */ new Date(now - 6e4)).toISOString(),
71
+ method: "GET",
72
+ path: "/pets",
73
+ statusCode: 200,
74
+ latencyMs: 42
75
+ },
76
+ {
77
+ occurredAtUtc: (/* @__PURE__ */ new Date(now - 12e4)).toISOString(),
78
+ method: "POST",
79
+ path: "/pets",
80
+ statusCode: 201,
81
+ latencyMs: 88
82
+ },
83
+ {
84
+ occurredAtUtc: (/* @__PURE__ */ new Date(now - 18e4)).toISOString(),
85
+ method: "GET",
86
+ path: "/pets/1",
87
+ statusCode: 404,
88
+ latencyMs: 31
89
+ }
90
+ ] };
91
+ }
92
+ function useUsage(range = "30d") {
93
+ const auth = useAuth();
94
+ const apiBaseUrl = useDevPortalApiBaseUrl();
95
+ const isPreview = useDevPortalPreviewMode();
96
+ const isDemoSession = useIsDemoSession();
97
+ const liveQuery = useQuery({
98
+ queryKey: [
99
+ "dev-portal-usage",
100
+ apiBaseUrl,
101
+ range
102
+ ],
103
+ enabled: auth.isAuthenticated && !isPreview && !isDemoSession && Boolean(apiBaseUrl),
104
+ queryFn: async () => {
105
+ const response = await fetch(`${apiBaseUrl}/api/v1/usage?range=${encodeURIComponent(range)}`, {
106
+ method: "GET",
107
+ headers: { Accept: "application/json" },
108
+ credentials: "include"
109
+ });
110
+ if (!response.ok) throw new Error("Failed to load usage.");
111
+ return await response.json();
112
+ }
113
+ });
114
+ if (isDemoSession && auth.isAuthenticated) return {
115
+ ...liveQuery,
116
+ data: createDemoUsage(range),
117
+ error: null,
118
+ isError: false,
119
+ isFetching: false,
120
+ isLoading: false,
121
+ isPending: false,
122
+ isSuccess: true,
123
+ status: "success"
124
+ };
125
+ return liveQuery;
126
+ }
127
+ function useUsageRecent(limit = 20) {
128
+ const auth = useAuth();
129
+ const apiBaseUrl = useDevPortalApiBaseUrl();
130
+ const isPreview = useDevPortalPreviewMode();
131
+ const isDemoSession = useIsDemoSession();
132
+ const liveQuery = useQuery({
133
+ queryKey: [
134
+ "dev-portal-usage-recent",
135
+ apiBaseUrl,
136
+ limit
137
+ ],
138
+ enabled: auth.isAuthenticated && !isPreview && !isDemoSession && Boolean(apiBaseUrl),
139
+ queryFn: async () => {
140
+ const response = await fetch(`${apiBaseUrl}/api/v1/usage/recent?limit=${limit}`, {
141
+ method: "GET",
142
+ headers: { Accept: "application/json" },
143
+ credentials: "include"
144
+ });
145
+ if (!response.ok) throw new Error("Failed to load recent requests.");
146
+ return await response.json();
147
+ }
148
+ });
149
+ if (isDemoSession && auth.isAuthenticated) return {
150
+ ...liveQuery,
151
+ data: createDemoRecent(),
152
+ error: null,
153
+ isError: false,
154
+ isFetching: false,
155
+ isLoading: false,
156
+ isPending: false,
157
+ isSuccess: true,
158
+ status: "success"
159
+ };
160
+ return liveQuery;
161
+ }
162
+
163
+ //#endregion
164
+ export { useUsageRecent as n, useUsage as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukoweb/apitogo-plugin-dev-portal-billing",
3
- "version": "0.2.30",
3
+ "version": "0.2.34",
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.30",
50
- "@lukoweb/apitogo-module-user-panel": "0.2.30"
49
+ "@lukoweb/apitogo": "0.2.34",
50
+ "@lukoweb/apitogo-module-user-panel": "0.2.34"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "react": ">=19.2.0",