@burdenoff/microfe-devportal 2026.623.5 → 2026.623.7
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/devportal/DevportalRoutes.js +123 -108
- package/dist/devportal/DevportalRoutes.js.map +1 -1
- package/dist/devportal/components/SectionNav.js +71 -5
- package/dist/devportal/components/SectionNav.js.map +1 -0
- package/dist/devportal/components/index.js +1 -1
- package/dist/devportal/hooks/useGraphQL.js +1 -1
- package/dist/devportal/pages/AdminReviewPage.js +39 -39
- package/dist/devportal/pages/AnalyticsDashboardPage.js +54 -54
- package/dist/devportal/pages/AnalyticsDashboardPage.js.map +1 -1
- package/dist/devportal/pages/ApiKeysPage.js +53 -53
- package/dist/devportal/pages/ApiKeysPage.js.map +1 -1
- package/dist/devportal/pages/AppLogsPage.js +9 -9
- package/dist/devportal/pages/ApplicationDetailPage.js +462 -462
- package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationVersionsPage.js +120 -120
- package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationsPage.js +73 -73
- package/dist/devportal/pages/ApplicationsPage.js.map +1 -1
- package/dist/devportal/pages/CreateApiKeyPage.js +20 -20
- package/dist/devportal/pages/CreateApplicationPage.js +32 -32
- package/dist/devportal/pages/CreateOAuthClientPage.js +18 -18
- package/dist/devportal/pages/CreateOrganizationPage.js +12 -12
- package/dist/devportal/pages/CreateWebhookPage.js +23 -23
- package/dist/devportal/pages/DashboardPage.js +23 -23
- package/dist/devportal/pages/DelegationsPage.js +22 -22
- package/dist/devportal/pages/EditApplicationPage.js +283 -283
- package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
- package/dist/devportal/pages/ListingPreviewPage.js +9 -9
- package/dist/devportal/pages/OAuthClientDetailPage.js +15 -15
- package/dist/devportal/pages/OAuthClientsPage.js +14 -14
- package/dist/devportal/pages/OrganizationDetailPage.js +84 -84
- package/dist/devportal/pages/OrganizationMembersPage.js +260 -260
- package/dist/devportal/pages/OrganizationMembersPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationsPage.js +16 -16
- package/dist/devportal/pages/PublishPage.js +16 -16
- package/dist/devportal/pages/PublisherProfilePage.js +18 -18
- package/dist/devportal/pages/RevenueDashboardPage.js +29 -29
- package/dist/devportal/pages/SettingsPage.js +17 -17
- package/dist/devportal/pages/SetupPage.js +99 -99
- package/dist/devportal/pages/SetupPage.js.map +1 -1
- package/dist/devportal/pages/WebhookDetailPage.js +143 -143
- package/dist/devportal/pages/WebhookDetailPage.js.map +1 -1
- package/dist/devportal/pages/WebhooksPage.js +133 -133
- package/dist/devportal/pages/WebhooksPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useDevportal as e } from "../providers/DevportalProvider.js";
|
|
2
2
|
import { EmptyState as t } from "../components/EmptyState.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { useApproveApplicationMutation as n, useGetApplicationForReviewQuery as r, useGetApplicationsQuery as i, useRejectApplicationMutation as a } from "../../generated/global-operations.js";
|
|
4
|
+
import { getErrorMessage as o } from "../hooks/useGraphQL.js";
|
|
5
|
+
import { PageLayout as s, PageSection as c } from "../components/PageLayout.js";
|
|
6
|
+
import { PermissionGuard as l } from "../components/PermissionGuard.js";
|
|
7
7
|
import "../components/index.js";
|
|
8
8
|
import { DEVPORTAL_PERMISSIONS as u } from "../constants/permissions.js";
|
|
9
9
|
import { useDevportalPermission as d } from "../hooks/usePermission.js";
|
|
@@ -24,11 +24,11 @@ var D = ({ manifest: e, tr: t }) => {
|
|
|
24
24
|
className: "mt-2 max-h-48 overflow-auto rounded-md bg-muted p-3 text-xs font-mono whitespace-pre-wrap break-all",
|
|
25
25
|
children: JSON.stringify(e, null, 2)
|
|
26
26
|
})] });
|
|
27
|
-
}, O = ({ app: t, onApprove: n, onReject:
|
|
27
|
+
}, O = ({ app: t, onApprove: n, onReject: i, processing: a }) => {
|
|
28
28
|
let { navigate: o, basePath: s } = e(), { t: c } = p(), l = (e, t) => {
|
|
29
29
|
let n = c(e);
|
|
30
30
|
return n === e ? t : n;
|
|
31
|
-
}, [m, v] = f(!1), [x, C] = f(""), O = d(u.APP_APPROVE), k = d(u.APP_REJECT), { data: A } =
|
|
31
|
+
}, [m, v] = f(!1), [x, C] = f(""), O = d(u.APP_APPROVE), k = d(u.APP_REJECT), { data: A } = r({
|
|
32
32
|
variables: { id: t.id },
|
|
33
33
|
skip: !m,
|
|
34
34
|
fetchPolicy: "cache-first"
|
|
@@ -224,11 +224,11 @@ var D = ({ manifest: e, tr: t }) => {
|
|
|
224
224
|
className: "flex gap-3",
|
|
225
225
|
children: [/* @__PURE__ */ E("button", {
|
|
226
226
|
onClick: () => n(t.id, x),
|
|
227
|
-
disabled:
|
|
228
|
-
"aria-disabled":
|
|
227
|
+
disabled: a || !O.allowed,
|
|
228
|
+
"aria-disabled": a || !O.allowed,
|
|
229
229
|
title: O.allowed ? void 0 : l("devportal.common.noPermission", "You do not have permission to perform this action"),
|
|
230
230
|
className: "inline-flex items-center gap-2 rounded-md bg-status-success-bg px-4 py-2 text-sm font-medium text-white hover:bg-status-success-bg/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary",
|
|
231
|
-
children: [
|
|
231
|
+
children: [a ? /* @__PURE__ */ T(S, {
|
|
232
232
|
size: 14,
|
|
233
233
|
className: "animate-spin",
|
|
234
234
|
"aria-hidden": "true"
|
|
@@ -237,12 +237,12 @@ var D = ({ manifest: e, tr: t }) => {
|
|
|
237
237
|
"aria-hidden": "true"
|
|
238
238
|
}), l("devportal.approve", "Approve")]
|
|
239
239
|
}), /* @__PURE__ */ E("button", {
|
|
240
|
-
onClick: () =>
|
|
241
|
-
disabled:
|
|
242
|
-
"aria-disabled":
|
|
240
|
+
onClick: () => i(t.id, x),
|
|
241
|
+
disabled: a || !x.trim() || !k.allowed,
|
|
242
|
+
"aria-disabled": a || !x.trim() || !k.allowed,
|
|
243
243
|
className: "inline-flex items-center gap-2 rounded-md bg-destructive px-4 py-2 text-sm font-medium text-destructive-foreground hover:bg-destructive/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary",
|
|
244
244
|
title: k.allowed ? x.trim() ? void 0 : l("devportal.notesRequiredForRejection", "Notes required for rejection") : l("devportal.common.noPermission", "You do not have permission to perform this action"),
|
|
245
|
-
children: [
|
|
245
|
+
children: [a ? /* @__PURE__ */ T(S, {
|
|
246
246
|
size: 14,
|
|
247
247
|
className: "animate-spin",
|
|
248
248
|
"aria-hidden": "true"
|
|
@@ -256,19 +256,19 @@ var D = ({ manifest: e, tr: t }) => {
|
|
|
256
256
|
})]
|
|
257
257
|
});
|
|
258
258
|
}, k = () => {
|
|
259
|
-
let { t: e } = p(),
|
|
259
|
+
let { t: e } = p(), r = (t, n) => {
|
|
260
260
|
let r = e(t);
|
|
261
261
|
return r === t ? n : r;
|
|
262
262
|
}, [d, g] = f(""), [_, y] = f(/* @__PURE__ */ new Set()), [b, x] = f(null), [w, D] = f(null), k = (e) => y((t) => new Set(t).add(e)), A = (e) => y((t) => {
|
|
263
263
|
let n = new Set(t);
|
|
264
264
|
return n.delete(e), n;
|
|
265
|
-
}), { data: j, loading: M, error: N, refetch: P } =
|
|
265
|
+
}), { data: j, loading: M, error: N, refetch: P } = i({
|
|
266
266
|
variables: {
|
|
267
267
|
status: "SUBMITTED",
|
|
268
268
|
limit: 100
|
|
269
269
|
},
|
|
270
270
|
fetchPolicy: "cache-and-network"
|
|
271
|
-
}), [F] =
|
|
271
|
+
}), [F] = n(), [I] = a(), L = j?.applications?.edges?.map((e) => e.node) ?? [], R = d ? L.filter((e) => e.name.toLowerCase().includes(d.toLowerCase()) || (e.shortDescription ?? "").toLowerCase().includes(d.toLowerCase())) : L, z = async (e, t) => {
|
|
272
272
|
k(e), x(null), D(null);
|
|
273
273
|
try {
|
|
274
274
|
let n = await F({ variables: {
|
|
@@ -276,9 +276,9 @@ var D = ({ manifest: e, tr: t }) => {
|
|
|
276
276
|
notes: t || void 0
|
|
277
277
|
} });
|
|
278
278
|
if (n.error) throw n.error;
|
|
279
|
-
D(
|
|
279
|
+
D(r("devportal.applicationApprovedSuccess", "Application approved successfully.")), await P();
|
|
280
280
|
} catch (e) {
|
|
281
|
-
x(e instanceof Error ? e.message :
|
|
281
|
+
x(e instanceof Error ? e.message : r("devportal.failedToApproveApplication", "Failed to approve application"));
|
|
282
282
|
} finally {
|
|
283
283
|
A(e);
|
|
284
284
|
}
|
|
@@ -291,48 +291,48 @@ var D = ({ manifest: e, tr: t }) => {
|
|
|
291
291
|
notes: t
|
|
292
292
|
} });
|
|
293
293
|
if (n.error) throw n.error;
|
|
294
|
-
D(
|
|
294
|
+
D(r("devportal.applicationRejected", "Application rejected.")), await P();
|
|
295
295
|
} catch (e) {
|
|
296
|
-
x(e instanceof Error ? e.message :
|
|
296
|
+
x(e instanceof Error ? e.message : r("devportal.failedToRejectApplication", "Failed to reject application"));
|
|
297
297
|
} finally {
|
|
298
298
|
A(e);
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
301
|
};
|
|
302
|
-
return M && !j ? /* @__PURE__ */ T(
|
|
303
|
-
title:
|
|
304
|
-
description:
|
|
302
|
+
return M && !j ? /* @__PURE__ */ T(s, {
|
|
303
|
+
title: r("devportal.appReviewQueue", "App Review Queue"),
|
|
304
|
+
description: r("devportal.appReviewQueueDescription", "Review submitted applications for approval"),
|
|
305
305
|
children: /* @__PURE__ */ T("div", {
|
|
306
306
|
className: "flex items-center justify-center py-12",
|
|
307
307
|
children: /* @__PURE__ */ T(S, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
308
308
|
})
|
|
309
|
-
}) : N ? /* @__PURE__ */ T(
|
|
310
|
-
title:
|
|
311
|
-
description:
|
|
309
|
+
}) : N ? /* @__PURE__ */ T(s, {
|
|
310
|
+
title: r("devportal.appReviewQueue", "App Review Queue"),
|
|
311
|
+
description: r("devportal.appReviewQueueDescription", "Review submitted applications for approval"),
|
|
312
312
|
children: /* @__PURE__ */ E("div", {
|
|
313
313
|
className: "rounded-lg border border-destructive/50 bg-destructive/10 p-6 text-center",
|
|
314
314
|
children: [
|
|
315
315
|
/* @__PURE__ */ T(m, { className: "mx-auto h-8 w-8 text-destructive" }),
|
|
316
316
|
/* @__PURE__ */ T("p", {
|
|
317
317
|
className: "mt-2 text-sm text-muted-foreground",
|
|
318
|
-
children:
|
|
318
|
+
children: o(N)
|
|
319
319
|
}),
|
|
320
320
|
/* @__PURE__ */ T("button", {
|
|
321
321
|
onClick: () => P(),
|
|
322
322
|
className: "mt-4 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90",
|
|
323
|
-
children:
|
|
323
|
+
children: r("devportal.retry", "Retry")
|
|
324
324
|
})
|
|
325
325
|
]
|
|
326
326
|
})
|
|
327
|
-
}) : /* @__PURE__ */ T(
|
|
327
|
+
}) : /* @__PURE__ */ T(l, {
|
|
328
328
|
permission: u.REVIEW_MANAGE,
|
|
329
|
-
children: /* @__PURE__ */ E(
|
|
330
|
-
title:
|
|
331
|
-
description:
|
|
329
|
+
children: /* @__PURE__ */ E(s, {
|
|
330
|
+
title: r("devportal.appReviewQueue", "App Review Queue"),
|
|
331
|
+
description: r("devportal.appReviewQueueFullDescription", "Review submitted applications for approval or rejection"),
|
|
332
332
|
actions: /* @__PURE__ */ T("button", {
|
|
333
333
|
onClick: () => P(),
|
|
334
334
|
className: "rounded-md border px-3 py-1.5 text-sm hover:bg-muted",
|
|
335
|
-
children:
|
|
335
|
+
children: r("devportal.refresh", "Refresh")
|
|
336
336
|
}),
|
|
337
337
|
children: [
|
|
338
338
|
/* @__PURE__ */ E("div", {
|
|
@@ -347,7 +347,7 @@ var D = ({ manifest: e, tr: t }) => {
|
|
|
347
347
|
children: [
|
|
348
348
|
L.length,
|
|
349
349
|
" ",
|
|
350
|
-
|
|
350
|
+
r("devportal.pendingReview", "pending review")
|
|
351
351
|
]
|
|
352
352
|
})]
|
|
353
353
|
}), _.size > 0 && /* @__PURE__ */ E("div", {
|
|
@@ -355,7 +355,7 @@ var D = ({ manifest: e, tr: t }) => {
|
|
|
355
355
|
children: [/* @__PURE__ */ T(S, {
|
|
356
356
|
size: 14,
|
|
357
357
|
className: "animate-spin"
|
|
358
|
-
}),
|
|
358
|
+
}), r("devportal.processing", "Processing...")]
|
|
359
359
|
})]
|
|
360
360
|
}),
|
|
361
361
|
w && /* @__PURE__ */ E("div", {
|
|
@@ -373,16 +373,16 @@ var D = ({ manifest: e, tr: t }) => {
|
|
|
373
373
|
className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground"
|
|
374
374
|
}), /* @__PURE__ */ T("input", {
|
|
375
375
|
type: "text",
|
|
376
|
-
placeholder:
|
|
376
|
+
placeholder: r("devportal.searchSubmittedApps", "Search submitted apps..."),
|
|
377
377
|
value: d,
|
|
378
378
|
onChange: (e) => g(e.target.value),
|
|
379
379
|
className: "w-full rounded-md border bg-background pl-9 pr-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary"
|
|
380
380
|
})]
|
|
381
381
|
}),
|
|
382
|
-
/* @__PURE__ */ T(
|
|
382
|
+
/* @__PURE__ */ T(c, { children: R.length === 0 ? /* @__PURE__ */ T(t, {
|
|
383
383
|
icon: /* @__PURE__ */ T(h, { size: 32 }),
|
|
384
|
-
title:
|
|
385
|
-
description: d ?
|
|
384
|
+
title: r("devportal.noAppsPendingReview", "No apps pending review"),
|
|
385
|
+
description: d ? r("devportal.noAppsMatchSearch", "No apps match your search") : r("devportal.allApplicationsReviewed", "All submitted applications have been reviewed")
|
|
386
386
|
}) : /* @__PURE__ */ T("div", {
|
|
387
387
|
className: "space-y-3",
|
|
388
388
|
children: R.map((e) => /* @__PURE__ */ T(O, {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { useDevportal as e } from "../providers/DevportalProvider.js";
|
|
2
2
|
import { EmptyState as t } from "../components/EmptyState.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
3
|
+
import { useGetAnalyticsDashboardOptionsQuery as n, useGetApplicationAnalyticsDashboardQuery as r } from "../../generated/global-operations.js";
|
|
4
|
+
import { getErrorMessage as i } from "../hooks/useGraphQL.js";
|
|
5
|
+
import { PageLayout as a, PageSection as o } from "../components/PageLayout.js";
|
|
6
|
+
import { StatCard as s, StatCardGrid as ee } from "../components/StatCard.js";
|
|
7
|
+
import { AppListItem as te } from "../components/AppCard.js";
|
|
8
|
+
import { DataTable as ne } from "../components/DataTable.js";
|
|
9
9
|
import "../hooks/index.js";
|
|
10
10
|
import { Tooltip as c } from "../../node_modules/.bun/recharts@2.15.4_21ccd8898788a04d/node_modules/recharts/es6/component/Tooltip.js";
|
|
11
11
|
import { ResponsiveContainer as l } from "../../node_modules/.bun/recharts@2.15.4_21ccd8898788a04d/node_modules/recharts/es6/component/ResponsiveContainer.js";
|
|
@@ -16,45 +16,45 @@ import { XAxis as p } from "../../node_modules/.bun/recharts@2.15.4_21ccd8898788
|
|
|
16
16
|
import { YAxis as m } from "../../node_modules/.bun/recharts@2.15.4_21ccd8898788a04d/node_modules/recharts/es6/cartesian/YAxis.js";
|
|
17
17
|
import { LineChart as re } from "../../node_modules/.bun/recharts@2.15.4_21ccd8898788a04d/node_modules/recharts/es6/chart/LineChart.js";
|
|
18
18
|
import { BarChart as ie } from "../../node_modules/.bun/recharts@2.15.4_21ccd8898788a04d/node_modules/recharts/es6/chart/BarChart.js";
|
|
19
|
-
import { useTr as
|
|
20
|
-
import { useEffect as
|
|
21
|
-
import { Activity as
|
|
19
|
+
import { useTr as ae } from "../utils/i18n.js";
|
|
20
|
+
import { useEffect as h, useMemo as g, useState as _ } from "react";
|
|
21
|
+
import { Activity as v, AlertCircle as y, AlertTriangle as b, BarChart3 as x, Clock as oe, Loader2 as S, Users as C } from "lucide-react";
|
|
22
22
|
import { useParams as w } from "react-router-dom";
|
|
23
23
|
import { Fragment as T, jsx as E, jsxs as D } from "react/jsx-runtime";
|
|
24
24
|
//#region src/devportal/pages/AnalyticsDashboardPage.tsx
|
|
25
|
-
var
|
|
25
|
+
var O = {
|
|
26
26
|
"7d": 7,
|
|
27
27
|
"30d": 30,
|
|
28
28
|
"90d": 90
|
|
29
|
-
},
|
|
29
|
+
}, k = {
|
|
30
30
|
primary: "var(--color-chart-1)",
|
|
31
31
|
secondary: "var(--color-chart-2)",
|
|
32
32
|
tertiary: "var(--color-chart-4)",
|
|
33
33
|
negative: "var(--color-chart-negative)"
|
|
34
|
-
},
|
|
35
|
-
let { appId:
|
|
34
|
+
}, A = () => {
|
|
35
|
+
let { appId: A } = w(), { navigate: se, basePath: ce, activeOrgId: j } = e(), M = ae(), [N, P] = _("30d"), [F, I] = _(A || ""), L = g(() => /* @__PURE__ */ new Date(), []), R = g(() => {
|
|
36
36
|
let e = new Date(L);
|
|
37
|
-
return e.setDate(e.getDate() -
|
|
38
|
-
}, [L, N]), { data: z, loading: B, error: V } =
|
|
39
|
-
skip: !!
|
|
37
|
+
return e.setDate(e.getDate() - O[N]), e.toISOString();
|
|
38
|
+
}, [L, N]), { data: z, loading: B, error: V } = n({
|
|
39
|
+
skip: !!A,
|
|
40
40
|
fetchPolicy: "cache-and-network"
|
|
41
|
-
}), { data: H, loading: U, error: W } =
|
|
41
|
+
}), { data: H, loading: U, error: W } = r({
|
|
42
42
|
variables: {
|
|
43
|
-
applicationId: F ||
|
|
43
|
+
applicationId: F || A || "",
|
|
44
44
|
period: "daily",
|
|
45
45
|
startDate: R,
|
|
46
46
|
endDate: L.toISOString()
|
|
47
47
|
},
|
|
48
|
-
skip: !(F ||
|
|
48
|
+
skip: !(F || A),
|
|
49
49
|
fetchPolicy: "cache-and-network"
|
|
50
50
|
}), G = z?.applications?.edges?.map((e) => e.node) ?? [], K = j ? G.filter((e) => e.developerOrganizationId === j) : G, q = H?.application;
|
|
51
|
-
|
|
52
|
-
let e = q?.name, t =
|
|
51
|
+
h(() => {
|
|
52
|
+
let e = q?.name, t = A || F;
|
|
53
53
|
if (!t || !e) return;
|
|
54
54
|
let n = window.__breadcrumbRegistry;
|
|
55
55
|
return n?.register(t, e), () => n?.unregister(t);
|
|
56
56
|
}, [
|
|
57
|
-
|
|
57
|
+
A,
|
|
58
58
|
F,
|
|
59
59
|
q?.name
|
|
60
60
|
]);
|
|
@@ -70,16 +70,16 @@ var se = {
|
|
|
70
70
|
sessions: e.sessions,
|
|
71
71
|
avgResponseTime: e.avgResponseTime ?? 0
|
|
72
72
|
})), Q = Y && Y.apiCalls > 0 ? Y.errors / Y.apiCalls * 100 : 0, le = X && X.apiCalls > 0 ? X.errors / X.apiCalls * 100 : 0, $ = Y?.avgResponseTime && X?.avgResponseTime ? X.avgResponseTime - Y.avgResponseTime : void 0, ue = (e) => {
|
|
73
|
-
|
|
73
|
+
se?.(`${ce}${e}`);
|
|
74
74
|
};
|
|
75
|
-
return !
|
|
75
|
+
return !A && B && !z ? /* @__PURE__ */ E(a, {
|
|
76
76
|
title: M("devportal.analytics.title", "Analytics"),
|
|
77
77
|
description: M("devportal.analytics.description", "Explore live application usage and performance metrics"),
|
|
78
78
|
children: /* @__PURE__ */ E("div", {
|
|
79
79
|
className: "flex items-center justify-center py-16 text-muted-foreground",
|
|
80
80
|
children: /* @__PURE__ */ E(S, { className: "h-8 w-8 animate-spin" })
|
|
81
81
|
})
|
|
82
|
-
}) : !
|
|
82
|
+
}) : !A && V ? /* @__PURE__ */ E(a, {
|
|
83
83
|
title: M("devportal.analytics.title", "Analytics"),
|
|
84
84
|
description: M("devportal.analytics.description", "Explore live application usage and performance metrics"),
|
|
85
85
|
children: /* @__PURE__ */ E(t, {
|
|
@@ -88,23 +88,23 @@ var se = {
|
|
|
88
88
|
className: "text-status-error-text"
|
|
89
89
|
}),
|
|
90
90
|
title: M("devportal.analytics.errorLoadApps", "Unable to load applications"),
|
|
91
|
-
description:
|
|
91
|
+
description: i(V)
|
|
92
92
|
})
|
|
93
|
-
}) : !
|
|
93
|
+
}) : !A && !F ? /* @__PURE__ */ E(a, {
|
|
94
94
|
title: M("devportal.analytics.title", "Analytics"),
|
|
95
95
|
description: M("devportal.analytics.selectAppDescription", "Select an application to review live usage metrics from the public gateway"),
|
|
96
96
|
children: K.length > 0 ? /* @__PURE__ */ E("div", {
|
|
97
97
|
className: "space-y-2",
|
|
98
|
-
children: K.map((e) => /* @__PURE__ */ E(
|
|
98
|
+
children: K.map((e) => /* @__PURE__ */ E(te, {
|
|
99
99
|
app: e,
|
|
100
100
|
onClick: () => I(e.id)
|
|
101
101
|
}, e.id))
|
|
102
102
|
}) : /* @__PURE__ */ E(t, {
|
|
103
|
-
icon: /* @__PURE__ */ E(
|
|
103
|
+
icon: /* @__PURE__ */ E(x, { size: 32 }),
|
|
104
104
|
title: M("devportal.analytics.noAppsTitle", "No applications available"),
|
|
105
105
|
description: M("devportal.analytics.noAppsDescription", "Create an application before viewing analytics.")
|
|
106
106
|
})
|
|
107
|
-
}) : W ? /* @__PURE__ */ E(
|
|
107
|
+
}) : W ? /* @__PURE__ */ E(a, {
|
|
108
108
|
title: M("devportal.analytics.title", "Analytics"),
|
|
109
109
|
description: M("devportal.analytics.description", "Explore live application usage and performance metrics"),
|
|
110
110
|
backLink: F ? `/apps/${F}` : void 0,
|
|
@@ -115,16 +115,16 @@ var se = {
|
|
|
115
115
|
className: "text-status-error-text"
|
|
116
116
|
}),
|
|
117
117
|
title: M("devportal.analytics.errorLoadAnalytics", "Unable to load analytics"),
|
|
118
|
-
description:
|
|
118
|
+
description: i(W)
|
|
119
119
|
})
|
|
120
|
-
}) : /* @__PURE__ */ E(
|
|
121
|
-
title:
|
|
120
|
+
}) : /* @__PURE__ */ E(a, {
|
|
121
|
+
title: A ? M("devportal.analytics.appTitle", "Application Analytics") : M("devportal.analytics.title", "Analytics"),
|
|
122
122
|
description: q ? `${M("devportal.analytics.liveUsageFor", "Live usage and performance for")} ${q.name}` : M("devportal.analytics.description", "Explore live application usage and performance metrics"),
|
|
123
|
-
backLink:
|
|
123
|
+
backLink: A && q ? `/apps/${q.id}` : void 0,
|
|
124
124
|
backLabel: M("devportal.analytics.backToApplication", "Back to Application"),
|
|
125
125
|
actions: /* @__PURE__ */ D("div", {
|
|
126
126
|
className: "flex flex-wrap gap-2",
|
|
127
|
-
children: [!
|
|
127
|
+
children: [!A && K.length > 0 && /* @__PURE__ */ E("select", {
|
|
128
128
|
value: F,
|
|
129
129
|
onChange: (e) => I(e.target.value),
|
|
130
130
|
"aria-label": M("devportal.analytics.selectAppAria", "Select application"),
|
|
@@ -158,32 +158,32 @@ var se = {
|
|
|
158
158
|
className: "flex items-center justify-center py-16 text-muted-foreground",
|
|
159
159
|
children: /* @__PURE__ */ E(S, { className: "h-8 w-8 animate-spin" })
|
|
160
160
|
}) : J.length > 0 ? /* @__PURE__ */ D(T, { children: [
|
|
161
|
-
/* @__PURE__ */ D(
|
|
161
|
+
/* @__PURE__ */ D(ee, {
|
|
162
162
|
columns: 4,
|
|
163
163
|
children: [
|
|
164
|
-
/* @__PURE__ */ E(
|
|
164
|
+
/* @__PURE__ */ E(s, {
|
|
165
165
|
title: M("devportal.analytics.apiCalls", "API Calls"),
|
|
166
166
|
value: (Y?.apiCalls ?? 0).toLocaleString(),
|
|
167
|
-
icon: /* @__PURE__ */ E(
|
|
167
|
+
icon: /* @__PURE__ */ E(v, { size: 20 })
|
|
168
168
|
}),
|
|
169
|
-
/* @__PURE__ */ E(
|
|
169
|
+
/* @__PURE__ */ E(s, {
|
|
170
170
|
title: M("devportal.analytics.activeUsers", "Active Users"),
|
|
171
171
|
value: (Y?.activeUsers ?? 0).toLocaleString(),
|
|
172
172
|
icon: /* @__PURE__ */ E(C, { size: 20 })
|
|
173
173
|
}),
|
|
174
|
-
/* @__PURE__ */ E(
|
|
174
|
+
/* @__PURE__ */ E(s, {
|
|
175
175
|
title: M("devportal.analytics.errorRate", "Error Rate"),
|
|
176
176
|
value: `${Q.toFixed(2)}%`,
|
|
177
|
-
icon: /* @__PURE__ */ E(
|
|
177
|
+
icon: /* @__PURE__ */ E(b, { size: 20 }),
|
|
178
178
|
trend: X ? {
|
|
179
179
|
value: le - Q,
|
|
180
180
|
label: M("devportal.analytics.vsPrevious", "vs previous period")
|
|
181
181
|
} : void 0
|
|
182
182
|
}),
|
|
183
|
-
/* @__PURE__ */ E(
|
|
183
|
+
/* @__PURE__ */ E(s, {
|
|
184
184
|
title: M("devportal.analytics.avgResponseTime", "Avg Response Time"),
|
|
185
185
|
value: Y?.avgResponseTime ? `${Y.avgResponseTime}ms` : M("devportal.common.na", "N/A"),
|
|
186
|
-
icon: /* @__PURE__ */ E(
|
|
186
|
+
icon: /* @__PURE__ */ E(oe, { size: 20 }),
|
|
187
187
|
trend: typeof $ == "number" ? {
|
|
188
188
|
value: $,
|
|
189
189
|
label: M("devportal.analytics.fasterThanPrevious", "faster than previous")
|
|
@@ -193,7 +193,7 @@ var se = {
|
|
|
193
193
|
}),
|
|
194
194
|
/* @__PURE__ */ D("div", {
|
|
195
195
|
className: "grid gap-6 xl:grid-cols-2",
|
|
196
|
-
children: [/* @__PURE__ */ E(
|
|
196
|
+
children: [/* @__PURE__ */ E(o, {
|
|
197
197
|
title: M("devportal.analytics.trafficTrend", "Traffic trend"),
|
|
198
198
|
children: /* @__PURE__ */ E("div", {
|
|
199
199
|
className: "rounded-lg border bg-card p-4",
|
|
@@ -224,14 +224,14 @@ var se = {
|
|
|
224
224
|
/* @__PURE__ */ E(f, {
|
|
225
225
|
type: "monotone",
|
|
226
226
|
dataKey: "apiCalls",
|
|
227
|
-
stroke:
|
|
227
|
+
stroke: k.primary,
|
|
228
228
|
strokeWidth: 2,
|
|
229
229
|
dot: !1
|
|
230
230
|
}),
|
|
231
231
|
/* @__PURE__ */ E(f, {
|
|
232
232
|
type: "monotone",
|
|
233
233
|
dataKey: "errors",
|
|
234
|
-
stroke:
|
|
234
|
+
stroke: k.negative,
|
|
235
235
|
strokeWidth: 2,
|
|
236
236
|
dot: !1
|
|
237
237
|
})
|
|
@@ -239,7 +239,7 @@ var se = {
|
|
|
239
239
|
})
|
|
240
240
|
})
|
|
241
241
|
})
|
|
242
|
-
}), /* @__PURE__ */ E(
|
|
242
|
+
}), /* @__PURE__ */ E(o, {
|
|
243
243
|
title: M("devportal.analytics.userActivity", "User activity"),
|
|
244
244
|
children: /* @__PURE__ */ E("div", {
|
|
245
245
|
className: "rounded-lg border bg-card p-4",
|
|
@@ -269,7 +269,7 @@ var se = {
|
|
|
269
269
|
} }),
|
|
270
270
|
/* @__PURE__ */ E(u, {
|
|
271
271
|
dataKey: "activeUsers",
|
|
272
|
-
fill:
|
|
272
|
+
fill: k.primary,
|
|
273
273
|
radius: [
|
|
274
274
|
6,
|
|
275
275
|
6,
|
|
@@ -279,7 +279,7 @@ var se = {
|
|
|
279
279
|
}),
|
|
280
280
|
/* @__PURE__ */ E(u, {
|
|
281
281
|
dataKey: "newUsers",
|
|
282
|
-
fill:
|
|
282
|
+
fill: k.secondary,
|
|
283
283
|
radius: [
|
|
284
284
|
6,
|
|
285
285
|
6,
|
|
@@ -293,9 +293,9 @@ var se = {
|
|
|
293
293
|
})
|
|
294
294
|
})]
|
|
295
295
|
}),
|
|
296
|
-
/* @__PURE__ */ E(
|
|
296
|
+
/* @__PURE__ */ E(o, {
|
|
297
297
|
title: M("devportal.analytics.dailyBreakdown", "Daily breakdown"),
|
|
298
|
-
children: /* @__PURE__ */ E(
|
|
298
|
+
children: /* @__PURE__ */ E(ne, {
|
|
299
299
|
data: J,
|
|
300
300
|
keyField: "id",
|
|
301
301
|
columns: [
|
|
@@ -342,10 +342,10 @@ var se = {
|
|
|
342
342
|
})
|
|
343
343
|
})
|
|
344
344
|
] }) : /* @__PURE__ */ E(t, {
|
|
345
|
-
icon: /* @__PURE__ */ E(
|
|
345
|
+
icon: /* @__PURE__ */ E(x, { size: 32 }),
|
|
346
346
|
title: M("devportal.analytics.noAnalyticsTitle", "No analytics available yet"),
|
|
347
347
|
description: M("devportal.analytics.noAnalyticsDescription", "Analytics will appear after this application starts receiving traffic through the public gateway."),
|
|
348
|
-
action:
|
|
348
|
+
action: A ? void 0 : /* @__PURE__ */ E("button", {
|
|
349
349
|
type: "button",
|
|
350
350
|
onClick: () => ue("/apps"),
|
|
351
351
|
className: "inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary",
|
|
@@ -355,6 +355,6 @@ var se = {
|
|
|
355
355
|
});
|
|
356
356
|
};
|
|
357
357
|
//#endregion
|
|
358
|
-
export {
|
|
358
|
+
export { A as AnalyticsDashboardPage };
|
|
359
359
|
|
|
360
360
|
//# sourceMappingURL=AnalyticsDashboardPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsDashboardPage.js","names":[],"sources":["../../../src/devportal/pages/AnalyticsDashboardPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport {\n Activity,\n AlertCircle,\n AlertTriangle,\n BarChart3,\n Clock,\n Loader2,\n Users,\n} from \"lucide-react\";\nimport {\n Bar,\n BarChart,\n CartesianGrid,\n Line,\n LineChart,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { DataTable } from \"../components/DataTable\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { StatCard, StatCardGrid } from \"../components/StatCard\";\nimport { AppListItem } from \"../components/AppCard\";\nimport type { Application } from \"../types\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport {\n type GetAnalyticsDashboardOptionsQuery,\n getErrorMessage,\n useGetAnalyticsDashboardOptionsQuery,\n useGetApplicationAnalyticsDashboardQuery,\n} from \"../hooks\";\nimport { useTr } from \"../utils/i18n\";\n\ntype TimeRange = \"7d\" | \"30d\" | \"90d\";\n\nconst daysByRange: Record<TimeRange, number> = {\n \"7d\": 7,\n \"30d\": 30,\n \"90d\": 90,\n};\n\nconst chartColors = {\n primary: \"var(--color-chart-1)\",\n secondary: \"var(--color-chart-2)\",\n tertiary: \"var(--color-chart-4)\",\n negative: \"var(--color-chart-negative)\",\n};\n\nexport const AnalyticsDashboardPage: FC = () => {\n const { appId } = useParams<{ appId: string }>();\n const { navigate, basePath, activeOrgId } = useDevportal();\n const tr = useTr();\n const [timeRange, setTimeRange] = useState<TimeRange>(\"30d\");\n const [selectedApplicationId, setSelectedApplicationId] = useState(\n appId || \"\",\n );\n\n const now = useMemo(() => new Date(), []);\n const startDate = useMemo(() => {\n const nextDate = new Date(now);\n nextDate.setDate(nextDate.getDate() - daysByRange[timeRange]);\n return nextDate.toISOString();\n }, [now, timeRange]);\n\n const {\n data: optionsData,\n loading: optionsLoading,\n error: optionsError,\n } = useGetAnalyticsDashboardOptionsQuery({\n skip: !!appId,\n fetchPolicy: \"cache-and-network\",\n });\n\n const { data, loading, error } = useGetApplicationAnalyticsDashboardQuery({\n variables: {\n applicationId: selectedApplicationId || appId || \"\",\n period: \"daily\",\n startDate,\n endDate: now.toISOString(),\n },\n skip: !(selectedApplicationId || appId),\n fetchPolicy: \"cache-and-network\",\n });\n\n const allApplications: Application[] =\n optionsData?.applications?.edges?.map(\n (\n edge: GetAnalyticsDashboardOptionsQuery[\"applications\"][\"edges\"][number],\n ) => edge.node as Application,\n ) ?? [];\n // Scope the app picker to the active org when one is selected\n const applications = activeOrgId\n ? allApplications.filter((a) => a.developerOrganizationId === activeOrgId)\n : allApplications;\n const selectedApplication = data?.application;\n\n useEffect(() => {\n const name = selectedApplication?.name;\n const id = appId || selectedApplicationId;\n if (!id || !name) return;\n const reg = (\n window as unknown as {\n __breadcrumbRegistry?: {\n register: (id: string, name: string) => void;\n unregister: (id: string) => void;\n };\n }\n ).__breadcrumbRegistry;\n reg?.register(id, name);\n return () => reg?.unregister(id);\n }, [appId, selectedApplicationId, selectedApplication?.name]);\n\n const analytics = [...(data?.applicationAnalytics ?? [])].sort(\n (left, right) =>\n new Date(left.date).getTime() - new Date(right.date).getTime(),\n );\n\n const latest = analytics[analytics.length - 1];\n const previous = analytics[analytics.length - 2];\n const chartData = analytics.map((entry) => ({\n date: new Date(entry.date).toLocaleDateString(undefined, {\n month: \"short\",\n day: \"numeric\",\n }),\n apiCalls: entry.apiCalls,\n errors: entry.errors,\n activeUsers: entry.activeUsers,\n newUsers: entry.newUsers,\n sessions: entry.sessions,\n avgResponseTime: entry.avgResponseTime ?? 0,\n }));\n\n const errorRate =\n latest && latest.apiCalls > 0 ? (latest.errors / latest.apiCalls) * 100 : 0;\n const previousErrorRate =\n previous && previous.apiCalls > 0\n ? (previous.errors / previous.apiCalls) * 100\n : 0;\n const responseTrend =\n latest?.avgResponseTime && previous?.avgResponseTime\n ? previous.avgResponseTime - latest.avgResponseTime\n : undefined;\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n if (!appId && optionsLoading && !optionsData) {\n return (\n <PageLayout\n title={tr(\"devportal.analytics.title\", \"Analytics\")}\n description={tr(\n \"devportal.analytics.description\",\n \"Explore live application usage and performance metrics\",\n )}\n >\n <div className=\"flex items-center justify-center py-16 text-muted-foreground\">\n <Loader2 className=\"h-8 w-8 animate-spin\" />\n </div>\n </PageLayout>\n );\n }\n\n if (!appId && optionsError) {\n return (\n <PageLayout\n title={tr(\"devportal.analytics.title\", \"Analytics\")}\n description={tr(\n \"devportal.analytics.description\",\n \"Explore live application usage and performance metrics\",\n )}\n >\n <EmptyState\n icon={<AlertCircle size={32} className=\"text-status-error-text\" />}\n title={tr(\n \"devportal.analytics.errorLoadApps\",\n \"Unable to load applications\",\n )}\n description={getErrorMessage(optionsError)}\n />\n </PageLayout>\n );\n }\n\n if (!appId && !selectedApplicationId) {\n return (\n <PageLayout\n title={tr(\"devportal.analytics.title\", \"Analytics\")}\n description={tr(\n \"devportal.analytics.selectAppDescription\",\n \"Select an application to review live usage metrics from the public gateway\",\n )}\n >\n {applications.length > 0 ? (\n <div className=\"space-y-2\">\n {applications.map((application) => (\n <AppListItem\n key={application.id}\n app={application}\n onClick={() => setSelectedApplicationId(application.id)}\n />\n ))}\n </div>\n ) : (\n <EmptyState\n icon={<BarChart3 size={32} />}\n title={tr(\n \"devportal.analytics.noAppsTitle\",\n \"No applications available\",\n )}\n description={tr(\n \"devportal.analytics.noAppsDescription\",\n \"Create an application before viewing analytics.\",\n )}\n />\n )}\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title={tr(\"devportal.analytics.title\", \"Analytics\")}\n description={tr(\n \"devportal.analytics.description\",\n \"Explore live application usage and performance metrics\",\n )}\n backLink={\n selectedApplicationId ? `/apps/${selectedApplicationId}` : undefined\n }\n backLabel={tr(\n \"devportal.analytics.backToApplication\",\n \"Back to Application\",\n )}\n >\n <EmptyState\n icon={<AlertCircle size={32} className=\"text-status-error-text\" />}\n title={tr(\n \"devportal.analytics.errorLoadAnalytics\",\n \"Unable to load analytics\",\n )}\n description={getErrorMessage(error)}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={\n appId\n ? tr(\"devportal.analytics.appTitle\", \"Application Analytics\")\n : tr(\"devportal.analytics.title\", \"Analytics\")\n }\n description={\n selectedApplication\n ? `${tr(\"devportal.analytics.liveUsageFor\", \"Live usage and performance for\")} ${selectedApplication.name}`\n : tr(\n \"devportal.analytics.description\",\n \"Explore live application usage and performance metrics\",\n )\n }\n backLink={\n appId && selectedApplication\n ? `/apps/${selectedApplication.id}`\n : undefined\n }\n backLabel={tr(\n \"devportal.analytics.backToApplication\",\n \"Back to Application\",\n )}\n actions={\n <div className=\"flex flex-wrap gap-2\">\n {!appId && applications.length > 0 && (\n <select\n value={selectedApplicationId}\n onChange={(event) => setSelectedApplicationId(event.target.value)}\n aria-label={tr(\n \"devportal.analytics.selectAppAria\",\n \"Select application\",\n )}\n className=\"rounded-md border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n {applications.map((application) => (\n <option key={application.id} value={application.id}>\n {application.name}\n </option>\n ))}\n </select>\n )}\n <select\n value={timeRange}\n onChange={(event) => setTimeRange(event.target.value as TimeRange)}\n aria-label={tr(\n \"devportal.analytics.selectRangeAria\",\n \"Select time range\",\n )}\n className=\"rounded-md border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value=\"7d\">\n {tr(\"devportal.analytics.range.7d\", \"Last 7 days\")}\n </option>\n <option value=\"30d\">\n {tr(\"devportal.analytics.range.30d\", \"Last 30 days\")}\n </option>\n <option value=\"90d\">\n {tr(\"devportal.analytics.range.90d\", \"Last 90 days\")}\n </option>\n </select>\n </div>\n }\n >\n {loading && !analytics.length ? (\n <div className=\"flex items-center justify-center py-16 text-muted-foreground\">\n <Loader2 className=\"h-8 w-8 animate-spin\" />\n </div>\n ) : analytics.length > 0 ? (\n <>\n <StatCardGrid columns={4}>\n <StatCard\n title={tr(\"devportal.analytics.apiCalls\", \"API Calls\")}\n value={(latest?.apiCalls ?? 0).toLocaleString()}\n icon={<Activity size={20} />}\n />\n <StatCard\n title={tr(\"devportal.analytics.activeUsers\", \"Active Users\")}\n value={(latest?.activeUsers ?? 0).toLocaleString()}\n icon={<Users size={20} />}\n />\n <StatCard\n title={tr(\"devportal.analytics.errorRate\", \"Error Rate\")}\n value={`${errorRate.toFixed(2)}%`}\n icon={<AlertTriangle size={20} />}\n trend={\n previous\n ? {\n value: previousErrorRate - errorRate,\n label: tr(\n \"devportal.analytics.vsPrevious\",\n \"vs previous period\",\n ),\n }\n : undefined\n }\n />\n <StatCard\n title={tr(\n \"devportal.analytics.avgResponseTime\",\n \"Avg Response Time\",\n )}\n value={\n latest?.avgResponseTime\n ? `${latest.avgResponseTime}ms`\n : tr(\"devportal.common.na\", \"N/A\")\n }\n icon={<Clock size={20} />}\n trend={\n typeof responseTrend === \"number\"\n ? {\n value: responseTrend,\n label: tr(\n \"devportal.analytics.fasterThanPrevious\",\n \"faster than previous\",\n ),\n }\n : undefined\n }\n />\n </StatCardGrid>\n\n <div className=\"grid gap-6 xl:grid-cols-2\">\n <PageSection\n title={tr(\"devportal.analytics.trafficTrend\", \"Traffic trend\")}\n >\n <div className=\"rounded-lg border bg-card p-4\">\n <ResponsiveContainer width=\"100%\" height={300}>\n <LineChart data={chartData}>\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"var(--color-chart-grid)\"\n />\n <XAxis\n dataKey=\"date\"\n stroke=\"var(--color-chart-axis)\"\n fontSize={12}\n />\n <YAxis stroke=\"var(--color-chart-axis)\" fontSize={12} />\n <Tooltip\n contentStyle={{\n backgroundColor: \"var(--color-card)\",\n border: \"1px solid var(--color-border-default)\",\n borderRadius: 12,\n }}\n />\n <Line\n type=\"monotone\"\n dataKey=\"apiCalls\"\n stroke={chartColors.primary}\n strokeWidth={2}\n dot={false}\n />\n <Line\n type=\"monotone\"\n dataKey=\"errors\"\n stroke={chartColors.negative}\n strokeWidth={2}\n dot={false}\n />\n </LineChart>\n </ResponsiveContainer>\n </div>\n </PageSection>\n\n <PageSection\n title={tr(\"devportal.analytics.userActivity\", \"User activity\")}\n >\n <div className=\"rounded-lg border bg-card p-4\">\n <ResponsiveContainer width=\"100%\" height={300}>\n <BarChart data={chartData}>\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"var(--color-chart-grid)\"\n />\n <XAxis\n dataKey=\"date\"\n stroke=\"var(--color-chart-axis)\"\n fontSize={12}\n />\n <YAxis stroke=\"var(--color-chart-axis)\" fontSize={12} />\n <Tooltip\n contentStyle={{\n backgroundColor: \"var(--color-card)\",\n border: \"1px solid var(--color-border-default)\",\n borderRadius: 12,\n }}\n />\n <Bar\n dataKey=\"activeUsers\"\n fill={chartColors.primary}\n radius={[6, 6, 0, 0]}\n />\n <Bar\n dataKey=\"newUsers\"\n fill={chartColors.secondary}\n radius={[6, 6, 0, 0]}\n />\n </BarChart>\n </ResponsiveContainer>\n </div>\n </PageSection>\n </div>\n\n <PageSection\n title={tr(\"devportal.analytics.dailyBreakdown\", \"Daily breakdown\")}\n >\n <DataTable\n data={analytics}\n keyField=\"id\"\n columns={[\n {\n key: \"date\",\n header: tr(\"devportal.analytics.date\", \"Date\"),\n render: (entry) =>\n new Date(entry.date).toLocaleDateString(undefined, {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n }),\n },\n {\n key: \"apiCalls\",\n header: tr(\"devportal.analytics.apiCalls\", \"API Calls\"),\n render: (entry) => entry.apiCalls.toLocaleString(),\n },\n {\n key: \"errors\",\n header: tr(\"devportal.analytics.errors\", \"Errors\"),\n render: (entry) => entry.errors.toLocaleString(),\n },\n {\n key: \"activeUsers\",\n header: tr(\"devportal.analytics.activeUsers\", \"Active Users\"),\n render: (entry) => entry.activeUsers.toLocaleString(),\n },\n {\n key: \"newUsers\",\n header: tr(\"devportal.analytics.newUsers\", \"New Users\"),\n render: (entry) => entry.newUsers.toLocaleString(),\n },\n {\n key: \"avgResponseTime\",\n header: tr(\"devportal.analytics.avgResponse\", \"Avg Response\"),\n render: (entry) =>\n entry.avgResponseTime\n ? `${entry.avgResponseTime}ms`\n : tr(\"devportal.common.na\", \"N/A\"),\n },\n {\n key: \"sessions\",\n header: tr(\"devportal.analytics.sessions\", \"Sessions\"),\n render: (entry) => entry.sessions.toLocaleString(),\n },\n ]}\n />\n </PageSection>\n </>\n ) : (\n <EmptyState\n icon={<BarChart3 size={32} />}\n title={tr(\n \"devportal.analytics.noAnalyticsTitle\",\n \"No analytics available yet\",\n )}\n description={tr(\n \"devportal.analytics.noAnalyticsDescription\",\n \"Analytics will appear after this application starts receiving traffic through the public gateway.\",\n )}\n action={\n !appId ? (\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"/apps\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n >\n {tr(\n \"devportal.analytics.viewApplications\",\n \"View applications\",\n )}\n </button>\n ) : undefined\n }\n />\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AnalyticsDashboardPage.js","names":[],"sources":["../../../src/devportal/pages/AnalyticsDashboardPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport {\n Activity,\n AlertCircle,\n AlertTriangle,\n BarChart3,\n Clock,\n Loader2,\n Users,\n} from \"lucide-react\";\nimport {\n Bar,\n BarChart,\n CartesianGrid,\n Line,\n LineChart,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { DataTable } from \"../components/DataTable\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { StatCard, StatCardGrid } from \"../components/StatCard\";\nimport { AppListItem } from \"../components/AppCard\";\nimport type { Application } from \"../types\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport {\n type GetAnalyticsDashboardOptionsQuery,\n getErrorMessage,\n useGetAnalyticsDashboardOptionsQuery,\n useGetApplicationAnalyticsDashboardQuery,\n} from \"../hooks\";\nimport { useTr } from \"../utils/i18n\";\n\ntype TimeRange = \"7d\" | \"30d\" | \"90d\";\n\nconst daysByRange: Record<TimeRange, number> = {\n \"7d\": 7,\n \"30d\": 30,\n \"90d\": 90,\n};\n\nconst chartColors = {\n primary: \"var(--color-chart-1)\",\n secondary: \"var(--color-chart-2)\",\n tertiary: \"var(--color-chart-4)\",\n negative: \"var(--color-chart-negative)\",\n};\n\nexport const AnalyticsDashboardPage: FC = () => {\n const { appId } = useParams<{ appId: string }>();\n const { navigate, basePath, activeOrgId } = useDevportal();\n const tr = useTr();\n const [timeRange, setTimeRange] = useState<TimeRange>(\"30d\");\n const [selectedApplicationId, setSelectedApplicationId] = useState(\n appId || \"\",\n );\n\n const now = useMemo(() => new Date(), []);\n const startDate = useMemo(() => {\n const nextDate = new Date(now);\n nextDate.setDate(nextDate.getDate() - daysByRange[timeRange]);\n return nextDate.toISOString();\n }, [now, timeRange]);\n\n const {\n data: optionsData,\n loading: optionsLoading,\n error: optionsError,\n } = useGetAnalyticsDashboardOptionsQuery({\n skip: !!appId,\n fetchPolicy: \"cache-and-network\",\n });\n\n const { data, loading, error } = useGetApplicationAnalyticsDashboardQuery({\n variables: {\n applicationId: selectedApplicationId || appId || \"\",\n period: \"daily\",\n startDate,\n endDate: now.toISOString(),\n },\n skip: !(selectedApplicationId || appId),\n fetchPolicy: \"cache-and-network\",\n });\n\n const allApplications: Application[] =\n optionsData?.applications?.edges?.map(\n (\n edge: GetAnalyticsDashboardOptionsQuery[\"applications\"][\"edges\"][number],\n ) => edge.node as Application,\n ) ?? [];\n // Scope the app picker to the active org when one is selected\n const applications = activeOrgId\n ? allApplications.filter((a) => a.developerOrganizationId === activeOrgId)\n : allApplications;\n const selectedApplication = data?.application;\n\n useEffect(() => {\n const name = selectedApplication?.name;\n const id = appId || selectedApplicationId;\n if (!id || !name) return;\n const reg = (\n window as unknown as {\n __breadcrumbRegistry?: {\n register: (id: string, name: string) => void;\n unregister: (id: string) => void;\n };\n }\n ).__breadcrumbRegistry;\n reg?.register(id, name);\n return () => reg?.unregister(id);\n }, [appId, selectedApplicationId, selectedApplication?.name]);\n\n const analytics = [...(data?.applicationAnalytics ?? [])].sort(\n (left, right) =>\n new Date(left.date).getTime() - new Date(right.date).getTime(),\n );\n\n const latest = analytics[analytics.length - 1];\n const previous = analytics[analytics.length - 2];\n const chartData = analytics.map((entry) => ({\n date: new Date(entry.date).toLocaleDateString(undefined, {\n month: \"short\",\n day: \"numeric\",\n }),\n apiCalls: entry.apiCalls,\n errors: entry.errors,\n activeUsers: entry.activeUsers,\n newUsers: entry.newUsers,\n sessions: entry.sessions,\n avgResponseTime: entry.avgResponseTime ?? 0,\n }));\n\n const errorRate =\n latest && latest.apiCalls > 0 ? (latest.errors / latest.apiCalls) * 100 : 0;\n const previousErrorRate =\n previous && previous.apiCalls > 0\n ? (previous.errors / previous.apiCalls) * 100\n : 0;\n const responseTrend =\n latest?.avgResponseTime && previous?.avgResponseTime\n ? previous.avgResponseTime - latest.avgResponseTime\n : undefined;\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n if (!appId && optionsLoading && !optionsData) {\n return (\n <PageLayout\n title={tr(\"devportal.analytics.title\", \"Analytics\")}\n description={tr(\n \"devportal.analytics.description\",\n \"Explore live application usage and performance metrics\",\n )}\n >\n <div className=\"flex items-center justify-center py-16 text-muted-foreground\">\n <Loader2 className=\"h-8 w-8 animate-spin\" />\n </div>\n </PageLayout>\n );\n }\n\n if (!appId && optionsError) {\n return (\n <PageLayout\n title={tr(\"devportal.analytics.title\", \"Analytics\")}\n description={tr(\n \"devportal.analytics.description\",\n \"Explore live application usage and performance metrics\",\n )}\n >\n <EmptyState\n icon={<AlertCircle size={32} className=\"text-status-error-text\" />}\n title={tr(\n \"devportal.analytics.errorLoadApps\",\n \"Unable to load applications\",\n )}\n description={getErrorMessage(optionsError)}\n />\n </PageLayout>\n );\n }\n\n if (!appId && !selectedApplicationId) {\n return (\n <PageLayout\n title={tr(\"devportal.analytics.title\", \"Analytics\")}\n description={tr(\n \"devportal.analytics.selectAppDescription\",\n \"Select an application to review live usage metrics from the public gateway\",\n )}\n >\n {applications.length > 0 ? (\n <div className=\"space-y-2\">\n {applications.map((application) => (\n <AppListItem\n key={application.id}\n app={application}\n onClick={() => setSelectedApplicationId(application.id)}\n />\n ))}\n </div>\n ) : (\n <EmptyState\n icon={<BarChart3 size={32} />}\n title={tr(\n \"devportal.analytics.noAppsTitle\",\n \"No applications available\",\n )}\n description={tr(\n \"devportal.analytics.noAppsDescription\",\n \"Create an application before viewing analytics.\",\n )}\n />\n )}\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title={tr(\"devportal.analytics.title\", \"Analytics\")}\n description={tr(\n \"devportal.analytics.description\",\n \"Explore live application usage and performance metrics\",\n )}\n backLink={\n selectedApplicationId ? `/apps/${selectedApplicationId}` : undefined\n }\n backLabel={tr(\n \"devportal.analytics.backToApplication\",\n \"Back to Application\",\n )}\n >\n <EmptyState\n icon={<AlertCircle size={32} className=\"text-status-error-text\" />}\n title={tr(\n \"devportal.analytics.errorLoadAnalytics\",\n \"Unable to load analytics\",\n )}\n description={getErrorMessage(error)}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={\n appId\n ? tr(\"devportal.analytics.appTitle\", \"Application Analytics\")\n : tr(\"devportal.analytics.title\", \"Analytics\")\n }\n description={\n selectedApplication\n ? `${tr(\"devportal.analytics.liveUsageFor\", \"Live usage and performance for\")} ${selectedApplication.name}`\n : tr(\n \"devportal.analytics.description\",\n \"Explore live application usage and performance metrics\",\n )\n }\n backLink={\n appId && selectedApplication\n ? `/apps/${selectedApplication.id}`\n : undefined\n }\n backLabel={tr(\n \"devportal.analytics.backToApplication\",\n \"Back to Application\",\n )}\n actions={\n <div className=\"flex flex-wrap gap-2\">\n {!appId && applications.length > 0 && (\n <select\n value={selectedApplicationId}\n onChange={(event) => setSelectedApplicationId(event.target.value)}\n aria-label={tr(\n \"devportal.analytics.selectAppAria\",\n \"Select application\",\n )}\n className=\"rounded-md border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n {applications.map((application) => (\n <option key={application.id} value={application.id}>\n {application.name}\n </option>\n ))}\n </select>\n )}\n <select\n value={timeRange}\n onChange={(event) => setTimeRange(event.target.value as TimeRange)}\n aria-label={tr(\n \"devportal.analytics.selectRangeAria\",\n \"Select time range\",\n )}\n className=\"rounded-md border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value=\"7d\">\n {tr(\"devportal.analytics.range.7d\", \"Last 7 days\")}\n </option>\n <option value=\"30d\">\n {tr(\"devportal.analytics.range.30d\", \"Last 30 days\")}\n </option>\n <option value=\"90d\">\n {tr(\"devportal.analytics.range.90d\", \"Last 90 days\")}\n </option>\n </select>\n </div>\n }\n >\n {loading && !analytics.length ? (\n <div className=\"flex items-center justify-center py-16 text-muted-foreground\">\n <Loader2 className=\"h-8 w-8 animate-spin\" />\n </div>\n ) : analytics.length > 0 ? (\n <>\n <StatCardGrid columns={4}>\n <StatCard\n title={tr(\"devportal.analytics.apiCalls\", \"API Calls\")}\n value={(latest?.apiCalls ?? 0).toLocaleString()}\n icon={<Activity size={20} />}\n />\n <StatCard\n title={tr(\"devportal.analytics.activeUsers\", \"Active Users\")}\n value={(latest?.activeUsers ?? 0).toLocaleString()}\n icon={<Users size={20} />}\n />\n <StatCard\n title={tr(\"devportal.analytics.errorRate\", \"Error Rate\")}\n value={`${errorRate.toFixed(2)}%`}\n icon={<AlertTriangle size={20} />}\n trend={\n previous\n ? {\n value: previousErrorRate - errorRate,\n label: tr(\n \"devportal.analytics.vsPrevious\",\n \"vs previous period\",\n ),\n }\n : undefined\n }\n />\n <StatCard\n title={tr(\n \"devportal.analytics.avgResponseTime\",\n \"Avg Response Time\",\n )}\n value={\n latest?.avgResponseTime\n ? `${latest.avgResponseTime}ms`\n : tr(\"devportal.common.na\", \"N/A\")\n }\n icon={<Clock size={20} />}\n trend={\n typeof responseTrend === \"number\"\n ? {\n value: responseTrend,\n label: tr(\n \"devportal.analytics.fasterThanPrevious\",\n \"faster than previous\",\n ),\n }\n : undefined\n }\n />\n </StatCardGrid>\n\n <div className=\"grid gap-6 xl:grid-cols-2\">\n <PageSection\n title={tr(\"devportal.analytics.trafficTrend\", \"Traffic trend\")}\n >\n <div className=\"rounded-lg border bg-card p-4\">\n <ResponsiveContainer width=\"100%\" height={300}>\n <LineChart data={chartData}>\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"var(--color-chart-grid)\"\n />\n <XAxis\n dataKey=\"date\"\n stroke=\"var(--color-chart-axis)\"\n fontSize={12}\n />\n <YAxis stroke=\"var(--color-chart-axis)\" fontSize={12} />\n <Tooltip\n contentStyle={{\n backgroundColor: \"var(--color-card)\",\n border: \"1px solid var(--color-border-default)\",\n borderRadius: 12,\n }}\n />\n <Line\n type=\"monotone\"\n dataKey=\"apiCalls\"\n stroke={chartColors.primary}\n strokeWidth={2}\n dot={false}\n />\n <Line\n type=\"monotone\"\n dataKey=\"errors\"\n stroke={chartColors.negative}\n strokeWidth={2}\n dot={false}\n />\n </LineChart>\n </ResponsiveContainer>\n </div>\n </PageSection>\n\n <PageSection\n title={tr(\"devportal.analytics.userActivity\", \"User activity\")}\n >\n <div className=\"rounded-lg border bg-card p-4\">\n <ResponsiveContainer width=\"100%\" height={300}>\n <BarChart data={chartData}>\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"var(--color-chart-grid)\"\n />\n <XAxis\n dataKey=\"date\"\n stroke=\"var(--color-chart-axis)\"\n fontSize={12}\n />\n <YAxis stroke=\"var(--color-chart-axis)\" fontSize={12} />\n <Tooltip\n contentStyle={{\n backgroundColor: \"var(--color-card)\",\n border: \"1px solid var(--color-border-default)\",\n borderRadius: 12,\n }}\n />\n <Bar\n dataKey=\"activeUsers\"\n fill={chartColors.primary}\n radius={[6, 6, 0, 0]}\n />\n <Bar\n dataKey=\"newUsers\"\n fill={chartColors.secondary}\n radius={[6, 6, 0, 0]}\n />\n </BarChart>\n </ResponsiveContainer>\n </div>\n </PageSection>\n </div>\n\n <PageSection\n title={tr(\"devportal.analytics.dailyBreakdown\", \"Daily breakdown\")}\n >\n <DataTable\n data={analytics}\n keyField=\"id\"\n columns={[\n {\n key: \"date\",\n header: tr(\"devportal.analytics.date\", \"Date\"),\n render: (entry) =>\n new Date(entry.date).toLocaleDateString(undefined, {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n }),\n },\n {\n key: \"apiCalls\",\n header: tr(\"devportal.analytics.apiCalls\", \"API Calls\"),\n render: (entry) => entry.apiCalls.toLocaleString(),\n },\n {\n key: \"errors\",\n header: tr(\"devportal.analytics.errors\", \"Errors\"),\n render: (entry) => entry.errors.toLocaleString(),\n },\n {\n key: \"activeUsers\",\n header: tr(\"devportal.analytics.activeUsers\", \"Active Users\"),\n render: (entry) => entry.activeUsers.toLocaleString(),\n },\n {\n key: \"newUsers\",\n header: tr(\"devportal.analytics.newUsers\", \"New Users\"),\n render: (entry) => entry.newUsers.toLocaleString(),\n },\n {\n key: \"avgResponseTime\",\n header: tr(\"devportal.analytics.avgResponse\", \"Avg Response\"),\n render: (entry) =>\n entry.avgResponseTime\n ? `${entry.avgResponseTime}ms`\n : tr(\"devportal.common.na\", \"N/A\"),\n },\n {\n key: \"sessions\",\n header: tr(\"devportal.analytics.sessions\", \"Sessions\"),\n render: (entry) => entry.sessions.toLocaleString(),\n },\n ]}\n />\n </PageSection>\n </>\n ) : (\n <EmptyState\n icon={<BarChart3 size={32} />}\n title={tr(\n \"devportal.analytics.noAnalyticsTitle\",\n \"No analytics available yet\",\n )}\n description={tr(\n \"devportal.analytics.noAnalyticsDescription\",\n \"Analytics will appear after this application starts receiving traffic through the public gateway.\",\n )}\n action={\n !appId ? (\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"/apps\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n >\n {tr(\n \"devportal.analytics.viewApplications\",\n \"View applications\",\n )}\n </button>\n ) : undefined\n }\n />\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|