diff --git a/src/app/routes/_authed/admin/telemetry.tsx b/src/app/routes/_authed/admin/telemetry.tsx index 6ba2abe..c4e6735 100644 --- a/src/app/routes/_authed/admin/telemetry.tsx +++ b/src/app/routes/_authed/admin/telemetry.tsx @@ -4,11 +4,11 @@ import { Trans } from "@lingui/react/macro"; import { msg } from "@lingui/core/macro"; const TAB_ROUTES = { + usage: "/admin/telemetry/usage", + players: "/admin/telemetry/players", functions: "/admin/telemetry/functions", events: "/admin/telemetry/events", errors: "/admin/telemetry/errors", - players: "/admin/telemetry/players", - usage: "/admin/telemetry/usage", } as const; type TabValue = keyof typeof TAB_ROUTES; @@ -29,7 +29,7 @@ function TelemetryLayout() { const activeTab = (Object.keys(TAB_ROUTES) as TabValue[]).find((tab) => pathname.endsWith(`/${tab}`) - ) ?? "functions"; + ) ?? "usage"; return ( + + Usage + + + Players + Functions @@ -50,12 +56,6 @@ function TelemetryLayout() { Errors - - Players - - - Usage - diff --git a/src/app/routes/_authed/admin/telemetry/index.tsx b/src/app/routes/_authed/admin/telemetry/index.tsx index e9553e9..67a876c 100644 --- a/src/app/routes/_authed/admin/telemetry/index.tsx +++ b/src/app/routes/_authed/admin/telemetry/index.tsx @@ -2,6 +2,6 @@ import { createFileRoute, redirect } from "@tanstack/react-router"; export const Route = createFileRoute("/_authed/admin/telemetry/")({ beforeLoad: () => { - throw redirect({ to: "/admin/telemetry/functions" }); + throw redirect({ to: "/admin/telemetry/usage" }); }, }); diff --git a/src/features/admin/components/dashboard/tools-list.tsx b/src/features/admin/components/dashboard/tools-list.tsx index c87b32b..0126df1 100644 --- a/src/features/admin/components/dashboard/tools-list.tsx +++ b/src/features/admin/components/dashboard/tools-list.tsx @@ -54,7 +54,7 @@ export const ToolsList = () => { string; +} + interface SparklineProps { data: number[]; series2?: number[]; @@ -11,6 +17,7 @@ interface SparklineProps { showArea?: boolean; label?: string; color?: string; + scale?: SparklineScale; } export const Sparkline = ({ @@ -20,6 +27,7 @@ export const Sparkline = ({ showArea = false, label, color = "var(--mantine-color-anchor)", + scale, }: SparklineProps) => { if (data.length === 0) { return ( @@ -38,7 +46,11 @@ export const Sparkline = ({ ? `${path} L${VIEW_WIDTH - PAD},${height - PAD} L${PAD},${height - PAD} Z` : null; - return ( + const allValues = series2?.length ? [...data, ...series2] : data; + const max = Math.max(...allValues); + const formatValue = scale?.formatValue ?? ((value: number) => value.toLocaleString()); + + const svg = ( ); + + if (!scale) return svg; + + return ( +
+ {svg} + + + {scale.start} + + + ↑{formatValue(max)} + + + {scale.end} + + +
+ ); }; export default Sparkline; diff --git a/src/features/telemetry/components/charts/stat-tile.tsx b/src/features/telemetry/components/charts/stat-tile.tsx index d0e22c2..4009dd5 100644 --- a/src/features/telemetry/components/charts/stat-tile.tsx +++ b/src/features/telemetry/components/charts/stat-tile.tsx @@ -1,21 +1,32 @@ import { ReactNode } from "react"; import { Box, Group, Stack, Text, UnstyledButton } from "@mantine/core"; -import Sparkline from "./sparkline"; +import Sparkline, { SparklineScale } from "./sparkline"; interface StatTileProps { label: ReactNode; value: ReactNode; + valueColor?: string; delta?: number; spark?: number[]; + sparkScale?: SparklineScale; caption?: ReactNode; onClick?: () => void; } -export const StatTile = ({ label, value, delta, spark, caption, onClick }: StatTileProps) => { +export const StatTile = ({ + label, + value, + valueColor, + delta, + spark, + sparkScale, + caption, + onClick, +}: StatTileProps) => { const content = ( - + {value} {delta !== undefined && delta !== 0 && ( @@ -29,7 +40,9 @@ export const StatTile = ({ label, value, delta, spark, caption, onClick }: StatT {label} {caption && {caption}} - {spark && spark.length > 0 && } + {spark && spark.length > 0 && ( + + )} ); diff --git a/src/features/telemetry/components/error-browser/error-browser.tsx b/src/features/telemetry/components/error-browser/error-browser.tsx index ded83ce..c5cd1bd 100644 --- a/src/features/telemetry/components/error-browser/error-browser.tsx +++ b/src/features/telemetry/components/error-browser/error-browser.tsx @@ -1,9 +1,7 @@ import { Suspense, useMemo, useState } from "react"; -import { useLingui } from "@lingui/react/macro"; -import { Container, Divider, Group, Stack, Switch } from "@mantine/core"; -import { ChecksIcon } from "@phosphor-icons/react"; +import { Trans, useLingui } from "@lingui/react/macro"; +import { Container, Divider, Group, Stack, Switch, Text } from "@mantine/core"; import { ListSkeleton } from "@/components/skeletons"; -import EmptyState from "@/components/empty-state"; import { useClientErrorGroups } from "../../queries"; import { lastNDays } from "../../range"; import { ErrorGroupRow } from "./error-group-row"; @@ -17,16 +15,13 @@ const ErrorGroupList = ({ to: string; includeResolved: boolean; }) => { - const { t } = useLingui(); const { data: groups } = useClientErrorGroups({ from, to, includeResolved }); if (groups.length === 0) { return ( - } - title={t`No unresolved errors`} - description={t`Nothing has broken in the last 30 days. Keep it up.`} - /> + + No unresolved errors + ); } diff --git a/src/features/telemetry/components/error-browser/error-group-row.tsx b/src/features/telemetry/components/error-browser/error-group-row.tsx index 9c260fa..1452aa5 100644 --- a/src/features/telemetry/components/error-browser/error-group-row.tsx +++ b/src/features/telemetry/components/error-browser/error-group-row.tsx @@ -1,7 +1,6 @@ import { useState } from "react"; import { Trans, useLingui } from "@lingui/react/macro"; import { - Badge, Box, Button, Code, @@ -42,9 +41,14 @@ export const ErrorGroupRow = ({ group }: { group: ClientErrorGroup }) => { > - - {group.count.toLocaleString()} - + + {group.count.toLocaleString()}× + {group.sample.message} @@ -85,7 +89,7 @@ export const ErrorGroupRow = ({ group }: { group: ClientErrorGroup }) => {