refactor(telemetry): chart scale labels, calmer vitals and error styling, usage-first tabs
- sparklines get start/end date and max-value footers - vitals rating shown by value color instead of badges - error rows use plain counts, subtle resolve button, terse empty state - telemetry tab order: usage, players, functions, events, errors; usage is default
This commit is contained in:
@@ -1,9 +1,15 @@
|
||||
import { Text } from "@mantine/core";
|
||||
import { Group, Text } from "@mantine/core";
|
||||
import { niceExtent, pointsToPath } from "./chart-path";
|
||||
|
||||
const VIEW_WIDTH = 240;
|
||||
const PAD = 3;
|
||||
|
||||
export interface SparklineScale {
|
||||
start?: string;
|
||||
end?: string;
|
||||
formatValue?: (value: number) => 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 = (
|
||||
<svg
|
||||
viewBox={`0 0 ${VIEW_WIDTH} ${height}`}
|
||||
preserveAspectRatio="none"
|
||||
@@ -72,6 +84,25 @@ export const Sparkline = ({
|
||||
)}
|
||||
</svg>
|
||||
);
|
||||
|
||||
if (!scale) return svg;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{svg}
|
||||
<Group justify="space-between" gap="xs" mt={2}>
|
||||
<Text size="xs" c="dimmed">
|
||||
{scale.start}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed" style={{ fontVariantNumeric: "tabular-nums" }}>
|
||||
↑{formatValue(max)}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed">
|
||||
{scale.end}
|
||||
</Text>
|
||||
</Group>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sparkline;
|
||||
|
||||
@@ -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 = (
|
||||
<Stack gap={2}>
|
||||
<Group gap="xs" align="baseline" wrap="nowrap">
|
||||
<Text fz="xl" fw={700} lh={1.2}>
|
||||
<Text fz="xl" fw={700} lh={1.2} c={valueColor}>
|
||||
{value}
|
||||
</Text>
|
||||
{delta !== undefined && delta !== 0 && (
|
||||
@@ -29,7 +40,9 @@ export const StatTile = ({ label, value, delta, spark, caption, onClick }: StatT
|
||||
{label}
|
||||
</Text>
|
||||
{caption && <Box>{caption}</Box>}
|
||||
{spark && spark.length > 0 && <Sparkline data={spark} height={28} />}
|
||||
{spark && spark.length > 0 && (
|
||||
<Sparkline data={spark} height={28} scale={sparkScale} />
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user