the vitest "something prevents Vite server from exiting" warning was @stylexjs/unplugin, not our code: configureServer starts a 150ms polling interval for HMR and clears it from server.httpServer's close event, which vitest resolves to null in middleware mode. drop that one hook under VITEST and keep the transform. textMuted was swept rather than judged. measured against their grounds, the 39 flat text-zinc-500 sites went 4.12:1 -> 7.56:1 in dark mode, repairing a WCAG AA failure, so that stands; the 4 already-adaptive sites went 7.40:1 -> 4.62:1 in light mode for nothing, so a textSecondary token restores their original zinc-600/zinc-400 pair. append the symbol families to the reset's font stack: Select renders U+25BE and Segoe UI does not carry it. correct two acceptance criteria that were literally false: both greps match only comments.
169 lines
4.9 KiB
TypeScript
169 lines
4.9 KiB
TypeScript
import { useState } from "react";
|
|
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
|
import * as stylex from "@stylexjs/stylex";
|
|
import { healthQuery, statsQuery, timeseriesQuery, upstreamHealthQuery } from "@/lib/queries";
|
|
import type { Period } from "@/lib/types";
|
|
import InlineError from "@/lib/InlineError";
|
|
import { styles as shared } from "@/ui/styles";
|
|
import { colors } from "@/ui/tokens.stylex";
|
|
import DiskCard from "./DiskCard";
|
|
import HealthBanners from "./HealthBanners";
|
|
import StatCards from "./StatCards";
|
|
import TimeseriesChart from "./TimeseriesChart";
|
|
import UpstreamHealthTable from "./UpstreamHealthTable";
|
|
|
|
const PERIODS: Period[] = ["1h", "24h", "7d", "30d"];
|
|
|
|
const styles = stylex.create({
|
|
page: {
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
gap: "1rem",
|
|
},
|
|
titleRow: {
|
|
display: "flex",
|
|
flexWrap: "wrap",
|
|
alignItems: "center",
|
|
justifyContent: "space-between",
|
|
gap: "0.75rem",
|
|
},
|
|
heading: {
|
|
fontSize: "1.5rem",
|
|
lineHeight: "2rem",
|
|
fontWeight: 600,
|
|
},
|
|
periodGroup: {
|
|
display: "flex",
|
|
gap: "0.25rem",
|
|
},
|
|
period: {
|
|
borderStyle: "none",
|
|
borderRadius: "0.25rem",
|
|
paddingInline: "0.625rem",
|
|
paddingBlock: "0.25rem",
|
|
fontSize: "0.875rem",
|
|
lineHeight: "1.25rem",
|
|
},
|
|
/** The pressed fill is heavier than `surfaceHover`, so a hover cannot mimic it. */
|
|
periodSelected: {
|
|
backgroundColor: {
|
|
default: "oklch(92% 0.004 286.32)",
|
|
"@media (prefers-color-scheme: dark)": "oklch(37% 0.013 285.805)",
|
|
},
|
|
color: colors.text,
|
|
fontWeight: 500,
|
|
},
|
|
periodIdle: {
|
|
backgroundColor: { default: "transparent", ":hover": colors.surfaceHover },
|
|
color: colors.textSecondary,
|
|
},
|
|
/** Dynamic: the caller sizes the placeholder to the widget it stands in for. */
|
|
skeletonHeight: (height: number) => ({ height }),
|
|
skeleton: {
|
|
borderRadius: "0.25rem",
|
|
backgroundColor: {
|
|
default: "oklch(92% 0.004 286.32)",
|
|
"@media (prefers-color-scheme: dark)": "oklch(27.4% 0.006 286.033)",
|
|
},
|
|
},
|
|
/** The chart takes two thirds beside the disk card from `lg`, one column below. */
|
|
panelGrid: {
|
|
display: "grid",
|
|
gap: "1rem",
|
|
gridTemplateColumns: {
|
|
default: "repeat(1, minmax(0, 1fr))",
|
|
"@media (min-width: 1024px)": "2fr 1fr",
|
|
},
|
|
},
|
|
panel: {
|
|
borderRadius: "0.25rem",
|
|
borderWidth: 1,
|
|
borderStyle: "solid",
|
|
borderColor: colors.border,
|
|
backgroundColor: colors.surfaceRaised,
|
|
paddingInline: "1rem",
|
|
paddingBlock: "0.75rem",
|
|
},
|
|
panelHeading: {
|
|
marginBottom: "0.75rem",
|
|
fontSize: "0.875rem",
|
|
lineHeight: "1.25rem",
|
|
fontWeight: 600,
|
|
},
|
|
});
|
|
|
|
function PeriodPicker({ period, onChange }: { period: Period; onChange: (period: Period) => void }) {
|
|
return (
|
|
<div role="group" aria-label="Period" {...stylex.props(styles.periodGroup)}>
|
|
{PERIODS.map((option) => (
|
|
<button
|
|
key={option}
|
|
type="button"
|
|
aria-pressed={option === period}
|
|
onClick={() => onChange(option)}
|
|
{...stylex.props(
|
|
styles.period,
|
|
option === period ? styles.periodSelected : styles.periodIdle,
|
|
shared.focusRing,
|
|
)}
|
|
>
|
|
{option}
|
|
</button>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function Skeleton({ height }: { height: number }) {
|
|
return <div aria-hidden="true" {...stylex.props(styles.skeleton, styles.skeletonHeight(height), shared.pulse)} />;
|
|
}
|
|
|
|
export default function DashboardPage() {
|
|
const [period, setPeriod] = useState<Period>("24h");
|
|
const stats = useQuery({ ...statsQuery(period), placeholderData: keepPreviousData });
|
|
const timeseries = useQuery({ ...timeseriesQuery(period), placeholderData: keepPreviousData });
|
|
const health = useQuery(healthQuery());
|
|
const upstreamHealth = useQuery(upstreamHealthQuery());
|
|
|
|
return (
|
|
<section {...stylex.props(styles.page)}>
|
|
<div {...stylex.props(styles.titleRow)}>
|
|
<h1 {...stylex.props(styles.heading)}>Dashboard</h1>
|
|
<PeriodPicker period={period} onChange={setPeriod} />
|
|
</div>
|
|
|
|
{health.data !== undefined && <HealthBanners health={health.data} />}
|
|
|
|
{stats.isError ? (
|
|
<InlineError error={stats.error} onRetry={() => void stats.refetch()} />
|
|
) : stats.data === undefined ? (
|
|
<Skeleton height={76} />
|
|
) : (
|
|
<StatCards stats={stats.data} />
|
|
)}
|
|
|
|
<div {...stylex.props(styles.panelGrid)}>
|
|
<section {...stylex.props(styles.panel)}>
|
|
<h2 {...stylex.props(styles.panelHeading)}>Queries over time</h2>
|
|
{timeseries.isError ? (
|
|
<InlineError error={timeseries.error} onRetry={() => void timeseries.refetch()} />
|
|
) : timeseries.data === undefined ? (
|
|
<Skeleton height={240} />
|
|
) : (
|
|
<TimeseriesChart data={timeseries.data} />
|
|
)}
|
|
</section>
|
|
{health.data === undefined ? <Skeleton height={160} /> : <DiskCard disk={health.data.disk} />}
|
|
</div>
|
|
|
|
{upstreamHealth.isError ? (
|
|
<InlineError error={upstreamHealth.error} onRetry={() => void upstreamHealth.refetch()} />
|
|
) : upstreamHealth.data === undefined ? (
|
|
<Skeleton height={120} />
|
|
) : (
|
|
<UpstreamHealthTable health={upstreamHealth.data} />
|
|
)}
|
|
</section>
|
|
);
|
|
}
|