fixes and improvements

This commit is contained in:
2026-05-07 12:34:16 +02:00
parent 2ecca84892
commit bba23165ab
14 changed files with 860 additions and 158 deletions
@@ -50,7 +50,43 @@ function formatRate(bytes: number): string {
function formatTime(epochSeconds: number | null): string {
if (!epochSeconds) return "-";
return new Date(epochSeconds * 1000).toLocaleString();
const date = new Date(epochSeconds * 1000);
const yyyy = date.getFullYear();
const mm = String(date.getMonth() + 1).padStart(2, "0");
const dd = String(date.getDate()).padStart(2, "0");
const hh = String(date.getHours()).padStart(2, "0");
const min = String(date.getMinutes()).padStart(2, "0");
return `${yyyy}-${mm}-${dd} ${hh}:${min}`;
}
function formatReadableTime(epochSeconds: number | null): string {
if (!epochSeconds) return "-";
const date = new Date(epochSeconds * 1000);
return date.toLocaleString("en-US", {
month: "short",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
hour12: false,
});
}
function formatClockTime(epochSeconds: number | null): string {
if (!epochSeconds) return "-";
const date = new Date(epochSeconds * 1000);
return date.toLocaleTimeString("en-US", {
hour: "2-digit",
minute: "2-digit",
hour12: false,
});
}
function formatUpdatedDetails(epochSeconds: number | null): [string, string] {
if (!epochSeconds) return ["-", "-"];
return [
formatReadableTime(epochSeconds),
`${formatClockTime(epochSeconds)} · ${formatAge(epochSeconds)}`,
];
}
function formatAge(epochSeconds: number | null): string {
@@ -111,40 +147,101 @@ function metricSortValue(
}
function metricCell(value: string, subtext?: string) {
const [minLine, maxLine] = (subtext || "").split("\n");
const min = minLine?.replace(/^Low\s+/, "").trim();
const max = maxLine?.replace(/^High\s+/, "").trim();
return (
<Stack
spacing={0.1}
sx={{
alignItems: "center",
justifyContent: "center",
width: "100%",
minWidth: 110,
height: "100%",
minHeight: 96,
textAlign: "center",
py: 0.4,
}}
>
<Typography
variant="body2"
<Box
sx={{
fontWeight: 700,
fontSize: "0.95rem",
lineHeight: 1.1,
flex: 1,
display: "flex",
alignItems: "center",
justifyContent: "center",
px: 1.5,
py: 1.15,
borderRadius: 1,
}}
>
{value}
</Typography>
{subtext ? (
<Typography
variant="caption"
color="text.secondary"
sx={{
whiteSpace: "pre-line",
lineHeight: 1.0,
fontSize: "0.64rem",
opacity: 0.9,
}}
>
{subtext}
</Typography>
) : null}
<Stack spacing={0.1} sx={{ alignItems: "center" }}>
<Typography
variant="caption"
color="text.secondary"
sx={{
fontSize: "0.62rem",
lineHeight: 1,
textTransform: "uppercase",
letterSpacing: "0.04em",
}}
>
10m avg
</Typography>
<Typography
variant="body1"
sx={{
fontWeight: 900,
fontSize: "1.14rem",
lineHeight: 1,
textAlign: "center",
whiteSpace: "nowrap",
fontVariantNumeric: "tabular-nums",
}}
>
{value}
</Typography>
</Stack>
</Box>
<Stack
spacing={0.25}
sx={{
width: "100%",
pt: 0.1,
}}
>
{min ? (
<Chip
size="small"
variant="outlined"
label={`Min ${min}`}
sx={{
width: "100%",
height: 18,
"& .MuiChip-label": {
px: 0.5,
py: 0,
fontSize: "0.6rem",
lineHeight: 1,
},
}}
/>
) : null}
{max ? (
<Chip
size="small"
variant="outlined"
label={`Max ${max}`}
sx={{
width: "100%",
height: 18,
"& .MuiChip-label": {
px: 0.5,
py: 0,
fontSize: "0.6rem",
lineHeight: 1,
},
}}
/>
) : null}
</Stack>
</Stack>
);
}
@@ -197,11 +294,7 @@ export function MonitoringOverviewTable({
size="small"
variant="outlined"
color={poller?.worker_running ? "success" : "default"}
label={
poller?.worker_running
? `Poller running · ${poller.interval_seconds}s`
: "Poller stopped"
}
label={poller?.worker_running ? "running" : "stopped"}
/>
<Chip
size="small"
@@ -230,12 +323,30 @@ export function MonitoringOverviewTable({
border: 1,
borderColor: "divider",
borderRadius: 1,
overflow: "hidden",
overflowX: "auto",
overflowY: "hidden",
}
: undefined
}
>
<Table size="small">
<Table
size="small"
sx={{
minWidth: 1280,
tableLayout: "fixed",
"& .MuiTableCell-root": {
px: 1.1,
py: 0.9,
verticalAlign: "middle",
},
"& .MuiTableHead .MuiTableCell-root": {
fontSize: "0.7rem",
fontWeight: 700,
lineHeight: 1.15,
whiteSpace: "nowrap",
},
}}
>
<TableHead>
<TableRow>
<TableCell
@@ -393,12 +504,6 @@ export function MonitoringOverviewTable({
sortedRows.map((row) => {
const machine = row.machine;
const status = row.status || row.status_error || "-";
const note =
row.metrics_error ||
row.disk_error ||
machine.notes ||
row.disk?.mount ||
"";
const cpu = formatSummary(
row.cpu_summary,
(value) => `${value.toFixed(1)}%`,
@@ -457,28 +562,28 @@ export function MonitoringOverviewTable({
label={status}
/>
</TableCell>
<TableCell align="right">
<TableCell align="center">
{metricCell(cpu.value, cpu.subtext)}
</TableCell>
<TableCell align="right">
<TableCell align="center">
{metricCell(iowait.value, iowait.subtext)}
</TableCell>
<TableCell align="right">
<TableCell align="center">
{metricCell(mem.value, mem.subtext)}
</TableCell>
<TableCell align="right">
<TableCell align="center">
{metricCell(netDown.value, netDown.subtext)}
</TableCell>
<TableCell align="right">
<TableCell align="center">
{metricCell(netUp.value, netUp.subtext)}
</TableCell>
<TableCell align="right">
<TableCell align="center">
{metricCell(diskRead.value, diskRead.subtext)}
</TableCell>
<TableCell align="right">
<TableCell align="center">
{metricCell(diskWrite.value, diskWrite.subtext)}
</TableCell>
<TableCell align="right">
<TableCell align="center">
{row.disk
? metricCell(
row.disk.used_pct,
@@ -487,22 +592,19 @@ export function MonitoringOverviewTable({
: "-"}
</TableCell>
<TableCell>
<Stack spacing={0.25}>
<Typography variant="caption" color="text.secondary">
{formatTime(row.latest_sample?.ts ?? null)}
</Typography>
{note && (
<Stack spacing={0.15} sx={{ alignItems: "center" }}>
{formatUpdatedDetails(
row.latest_sample?.ts ?? null,
).map((line) => (
<Typography
key={line}
variant="caption"
color={
row.metrics_error || row.disk_error
? "error.main"
: "text.secondary"
}
color="text.secondary"
sx={{ lineHeight: 1.1 }}
>
{note}
{line}
</Typography>
)}
))}
</Stack>
</TableCell>
</TableRow>