From 7664efc41d8b2a6156033cf3b4e02e70ef173cc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?O=C4=9Fuzhan=20Olguncu?= <21091016+ogzhanolguncu@users.noreply.github.com> Date: Thu, 9 Jan 2025 13:45:48 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20correct=20disabled=20stats=20metric=20di?= =?UTF-8?q?splaying=20valid=20count=20instead=20of=20=E2=80=A6=20(#2796)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: correct disabled stats metric displaying valid count instead of disabled * fix: maintain the order of verfication requests --- .../[apiId]/keys/[keyAuthId]/[keyId]/page.tsx | 39 ++++++++--- .../dashboard/components/dashboard/charts.tsx | 65 +++++++++++++++++-- 2 files changed, 90 insertions(+), 14 deletions(-) diff --git a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/page.tsx b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/page.tsx index f94ecb0bdd..72bfda7652 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/page.tsx +++ b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/page.tsx @@ -111,6 +111,9 @@ export default async function APIKeyDetailPage(props: { .then((res) => res.val?.at(0)?.time ?? 0), ]); + // Sort all verifications by time first + const sortedVerifications = verifications.val!.sort((a, b) => a.time - b.time); + const successOverTime: { x: string; y: number }[] = []; const ratelimitedOverTime: { x: string; y: number }[] = []; const usageExceededOverTime: { x: string; y: number }[] = []; @@ -119,30 +122,47 @@ export default async function APIKeyDetailPage(props: { const expiredOverTime: { x: string; y: number }[] = []; const forbiddenOverTime: { x: string; y: number }[] = []; - for (const d of verifications.val!.sort((a, b) => a.time - b.time)) { + // Get all unique timestamps + const uniqueDates = [...new Set(sortedVerifications.map((d) => d.time))].sort((a, b) => a - b); + + // Ensure each array has entries for all timestamps with zero counts + for (const timestamp of uniqueDates) { + const x = new Date(timestamp).toISOString(); + successOverTime.push({ x, y: 0 }); + ratelimitedOverTime.push({ x, y: 0 }); + usageExceededOverTime.push({ x, y: 0 }); + disabledOverTime.push({ x, y: 0 }); + insufficientPermissionsOverTime.push({ x, y: 0 }); + expiredOverTime.push({ x, y: 0 }); + forbiddenOverTime.push({ x, y: 0 }); + } + + for (const d of sortedVerifications) { const x = new Date(d.time).toISOString(); + const index = uniqueDates.indexOf(d.time); + switch (d.outcome) { case "": case "VALID": - successOverTime.push({ x, y: d.count }); + successOverTime[index] = { x, y: d.count }; break; case "RATE_LIMITED": - ratelimitedOverTime.push({ x, y: d.count }); + ratelimitedOverTime[index] = { x, y: d.count }; break; case "USAGE_EXCEEDED": - usageExceededOverTime.push({ x, y: d.count }); + usageExceededOverTime[index] = { x, y: d.count }; break; case "DISABLED": - disabledOverTime.push({ x, y: d.count }); + disabledOverTime[index] = { x, y: d.count }; break; case "INSUFFICIENT_PERMISSIONS": - insufficientPermissionsOverTime.push({ x, y: d.count }); + insufficientPermissionsOverTime[index] = { x, y: d.count }; break; case "EXPIRED": - expiredOverTime.push({ x, y: d.count }); + expiredOverTime[index] = { x, y: d.count }; break; case "FORBIDDEN": - forbiddenOverTime.push({ x, y: d.count }); + forbiddenOverTime[index] = { x, y: d.count }; break; } } @@ -209,6 +229,7 @@ export default async function APIKeyDetailPage(props: { stats.forbidden += v.count; } }); + const roleTee = key.workspace.roles.map((role) => { const nested: NestedPermissions = {}; for (const permission of key.workspace.permissions) { @@ -328,7 +349,7 @@ export default async function APIKeyDetailPage(props: { - + ) => { const { resolvedTheme } = useTheme(); - const colors: { light: Record; dark: Record } = { + const colors: { + light: Record; + dark: Record; + } = { light: { primary: "#1c1917", warn: "#FFCD07", @@ -133,9 +136,9 @@ export const LineChart: React.FC<{ tooltip={{ formatter: (datum) => ({ name: datum.category, - value: `${Intl.NumberFormat(undefined, { notation: "compact" }).format( - Number(datum.y), - )} ms`, + value: `${Intl.NumberFormat(undefined, { + notation: "compact", + }).format(Number(datum.y))} ms`, }), }} /> @@ -202,6 +205,53 @@ export const StackedColumnChart: React.FC<{ colors: Array; }> = ({ data, timeGranularity, colors }) => { const { axisColor } = useColors(colors); + + const formatDate = (date: string) => { + const d = new Date(date); + if (Number.isNaN(d.getTime())) { + return date; + } + + switch (timeGranularity) { + case "minute": + return d.toLocaleString(undefined, { + hour: "numeric", + minute: "2-digit", + hour12: true, + month: "short", + day: "numeric", + }); + case "hour": + return d.toLocaleString(undefined, { + hour: "numeric", + hour12: true, + month: "short", + day: "numeric", + year: "numeric", + }); + case "day": + return d.toLocaleString(undefined, { + weekday: "short", + month: "short", + day: "numeric", + year: "numeric", + }); + case "month": + return d.toLocaleString(undefined, { + month: "long", + year: "numeric", + }); + default: + return d.toLocaleString(undefined, { + month: "short", + day: "numeric", + year: "numeric", + hour: "numeric", + minute: "2-digit", + }); + } + }; + return ( ({ name: datum.category, - value: Intl.NumberFormat(undefined, { notation: "compact" }).format(Number(datum.y)), + value: Intl.NumberFormat(undefined, { + notation: "compact", + maximumFractionDigits: 1, + compactDisplay: "short", + }).format(Number(datum.y)), }), }} />