The Overview page takes the decided visual language (specs/ui-visual-redesign.md): four centred totals with their Activity links, a smoothed area chart of total and blocked queries with point hover and a tooltip beside the point, a stacked client chart in eight distinct hues plus one Other band, and a card row with the cache hit rate, the query types as a single-hue ramp ring, and the upstream breakdown. GET /api/overview takes a client parameter; the scoped read uses idx_query_log_ts and the cache keeps scoped slots. The device selector beside the period selector is URL state, so a scoped view is a link, and the tile links carry the scope into Activity. All numbers go through admin/src/lib/format.ts: grouped counts, two-decimal percentages, one-decimal rates, durations as the two largest nonzero units. A sweep test refuses toFixed, toLocaleString, Intl.NumberFormat and padStart anywhere else. Chrome: one 4px radius from the metrics constants, shared Card with a prominent title and description, the sidebar grouped into Monitoring and System with a status block (protection, queries per minute on Overview, uptime), and the accent darkened to 5.43:1 on its wash. Not built: the spec's ranked-list primitive, which has no consumer and no API rows. Codex could not review sessions B to D, its endpoint returned 404 for every call on 2026-09-03; the owner accepted the smoke-server screenshots.
127 lines
3.6 KiB
TypeScript
127 lines
3.6 KiB
TypeScript
/**
|
|
* The cache hit rate as a progress bar (ui-visual-redesign.md): the share of
|
|
* the window's queries answered from memory, the bar it fills, and under it the
|
|
* three figures that share went with — hits, what went upstream instead, and
|
|
* how long an answer took on average.
|
|
*/
|
|
|
|
import * as stylex from "@stylexjs/stylex";
|
|
import { formatCount, formatMicros, formatPercent } from "@/lib/format";
|
|
import Card from "@/ui/Card";
|
|
import { styles as shared } from "@/ui/styles";
|
|
import { colors } from "@/ui/tokens.stylex";
|
|
|
|
const styles = stylex.create({
|
|
body: {
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
gap: "0.75rem",
|
|
},
|
|
track: {
|
|
height: "0.5rem",
|
|
borderRadius: "999px",
|
|
backgroundColor: colors.chartGreenSurface,
|
|
overflow: "hidden",
|
|
},
|
|
fill: {
|
|
display: "block",
|
|
height: "100%",
|
|
borderRadius: "999px",
|
|
backgroundColor: colors.chartGreen,
|
|
},
|
|
/** Dynamic: the bar's length is the share itself. */
|
|
fillWidth: (percent: number) => ({ width: `${percent}%` }),
|
|
percent: {
|
|
margin: 0,
|
|
fontSize: "2.5rem",
|
|
lineHeight: 1,
|
|
fontWeight: 400,
|
|
letterSpacing: "-0.02em",
|
|
color: colors.chartGreen,
|
|
},
|
|
note: {
|
|
fontSize: "0.875rem",
|
|
lineHeight: "1.25rem",
|
|
color: colors.textMuted,
|
|
},
|
|
split: {
|
|
display: "flex",
|
|
flexWrap: "wrap",
|
|
gap: "1.5rem",
|
|
margin: 0,
|
|
marginTop: "0.5rem",
|
|
paddingTop: "1rem",
|
|
borderTopWidth: 1,
|
|
borderTopStyle: "solid",
|
|
borderTopColor: colors.border,
|
|
},
|
|
/** Label first in the DOM, figure on top visually. */
|
|
splitItem: {
|
|
display: "flex",
|
|
flexDirection: "column-reverse",
|
|
gap: "0.125rem",
|
|
},
|
|
splitValue: {
|
|
margin: 0,
|
|
fontSize: "1.125rem",
|
|
lineHeight: "1.5rem",
|
|
fontWeight: 550,
|
|
color: colors.text,
|
|
},
|
|
splitLabel: {
|
|
fontSize: "0.75rem",
|
|
lineHeight: "1rem",
|
|
color: colors.textMuted,
|
|
},
|
|
});
|
|
|
|
export interface CacheCardData {
|
|
queries: number;
|
|
/** Answers served from cache: the window's `cached` buckets summed. */
|
|
hits: number;
|
|
/** Answers that went to an upstream resolver or a forward zone. */
|
|
forwarded: number;
|
|
avg_response_time_us: number | null;
|
|
}
|
|
|
|
export default function CacheCard({ data }: { data: CacheCardData }) {
|
|
const share = data.queries === 0 ? null : data.hits / data.queries;
|
|
const percent = share === null ? "—" : formatPercent(share);
|
|
return (
|
|
<Card
|
|
title="Cache hit rate"
|
|
description="Answers served straight from memory, without asking an upstream resolver."
|
|
>
|
|
<div {...stylex.props(styles.body)}>
|
|
<div
|
|
role="img"
|
|
aria-label={
|
|
share === null ? "No queries in this period" : `${percent} of queries served from cache`
|
|
}
|
|
{...stylex.props(styles.track)}
|
|
>
|
|
<span {...stylex.props(styles.fill, styles.fillWidth(share === null ? 0 : share * 100))} />
|
|
</div>
|
|
<p {...stylex.props(styles.percent, shared.tabularNums)}>{percent}</p>
|
|
<p {...stylex.props(styles.note)}>of queries answered from cache</p>
|
|
<dl {...stylex.props(styles.split)}>
|
|
<div {...stylex.props(styles.splitItem)}>
|
|
<dt {...stylex.props(styles.splitLabel)}>cache hits</dt>
|
|
<dd {...stylex.props(styles.splitValue, shared.tabularNums)}>{formatCount(data.hits)}</dd>
|
|
</div>
|
|
<div {...stylex.props(styles.splitItem)}>
|
|
<dt {...stylex.props(styles.splitLabel)}>forwarded upstream</dt>
|
|
<dd {...stylex.props(styles.splitValue, shared.tabularNums)}>{formatCount(data.forwarded)}</dd>
|
|
</div>
|
|
<div {...stylex.props(styles.splitItem)}>
|
|
<dt {...stylex.props(styles.splitLabel)}>avg response</dt>
|
|
<dd {...stylex.props(styles.splitValue, shared.tabularNums)}>
|
|
{data.avg_response_time_us === null ? "—" : formatMicros(data.avg_response_time_us)}
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
</Card>
|
|
);
|
|
}
|