Serverpuls: deutsche Zeitachse und Legende im Diagramm

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-09-30 12:43:13 +00:00
1 parent 16f3150ff1
commit 57f235dd36
3 files changed
+60 -3

No files matched your search

+8 -2
View File
@@ -1,5 +1,6 @@
import { useEffect, useRef } from 'react'
import uPlot from 'uplot'
import { formatAxisTick, formatLegendTime } from '../../lib/serverpulsFormat'
import 'uplot/dist/uPlot.min.css'
/**
@@ -23,7 +24,12 @@ export default function Chart({ data, series, height = 280, yFmt, yRange }) {
legend: { live: true },
scales: { y: { range: yr || ((u, min, max) => [Math.min(0, min), max === min ? min + 1 : max + (max - min) * 0.08]) } },
axes: [
{ stroke: '#9aa7b4', grid: { stroke: 'rgba(255,255,255,0.06)' }, ticks: { stroke: 'rgba(255,255,255,0.1)' } },
{
stroke: '#9aa7b4',
grid: { stroke: 'rgba(255,255,255,0.06)' },
ticks: { stroke: 'rgba(255,255,255,0.1)' },
values: (u, splits, axisIdx, foundSpace, foundIncr) => splits.map((t) => formatAxisTick(t, foundIncr)),
},
{
stroke: '#9aa7b4',
grid: { stroke: 'rgba(255,255,255,0.06)' },
@@ -33,7 +39,7 @@ export default function Chart({ data, series, height = 280, yFmt, yRange }) {
},
],
series: [
{},
{ label: 'Zeit', value: (u, v) => formatLegendTime(v) },
...s.map((x) => ({
label: x.label,
stroke: x.color || '#10b981',
+33
View File
@@ -56,3 +56,36 @@ export function formatNumber(v, digits = 2) {
if (v === null || v === undefined) return '–'
return nf(digits).format(v)
}
// --- Zeitachsen (Europe/Berlin, de-DE) ---
const TZ = 'Europe/Berlin'
const dtf = (opts) => new Intl.DateTimeFormat('de-DE', { timeZone: TZ, ...opts })
const F = {
time: dtf({ hour: '2-digit', minute: '2-digit', hourCycle: 'h23' }),
day: dtf({ day: '2-digit', month: '2-digit' }),
month: dtf({ month: 'short', year: 'numeric' }),
year: dtf({ year: 'numeric' }),
legend: dtf({ day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', hourCycle: 'h23' }),
}
// Welches Format passt zu einem Tick-Abstand (Sekunden)?
export function axisTimeKind(incrSec) {
if (incrSec < 86400) return 'time'
if (incrSec < 86400 * 28) return 'day'
if (incrSec < 86400 * 360) return 'month'
return 'year'
}
// Tick (Sekunden) -> Text. Bei Tagesgrenzen innerhalb von Sub-Tages-Achsen das Datum zeigen.
export function formatAxisTick(sec, incrSec) {
const kind = axisTimeKind(incrSec)
const d = new Date(sec * 1000)
if (kind === 'time' && F.time.format(d) === '00:00') return F.day.format(d)
return F[kind].format(d)
}
// Legende: "30.09.2026, 11:20"
export function formatLegendTime(sec) {
if (sec === null || sec === undefined) return '–'
return F.legend.format(new Date(sec * 1000))
}
+19 -1
View File
@@ -2,7 +2,7 @@ import test from 'node:test'
import assert from 'node:assert/strict'
import { loadModule } from './load.mjs'
const { formatBytes, formatDelta, ageLabel, usedPct } = await loadModule('src/lib/serverpulsFormat.js')
const { formatBytes, formatDelta, ageLabel, usedPct, axisTimeKind, formatAxisTick, formatLegendTime } = await loadModule('src/lib/serverpulsFormat.js')
test('formatBytes: de-DE, Basis 1024', () => {
assert.equal(formatBytes(1288490189), '1,2 GB')
@@ -34,3 +34,21 @@ test('usedPct: RAM value ist frei', () => {
assert.equal(usedPct(250, 1000), 75)
assert.equal(usedPct(null, 1000), null)
})
test('Zeitachse: Format nach Tick-Abstand', () => {
assert.equal(axisTimeKind(3600), 'time')
assert.equal(axisTimeKind(86400), 'day')
assert.equal(axisTimeKind(86400 * 30), 'month')
assert.equal(axisTimeKind(86400 * 365), 'year')
})
test('Zeitachse: deutsche Ausgabe in Europe/Berlin', () => {
const t = Date.UTC(2026, 8, 30, 9, 20) / 1000 // 11:20 Berlin (MESZ)
assert.equal(formatAxisTick(t, 3600), '11:20')
assert.equal(formatAxisTick(t, 86400 * 2), '30.09.')
assert.equal(formatAxisTick(Date.UTC(2026, 8, 29, 22, 0) / 1000, 3600 * 6), '30.09.') // Mitternacht Berlin
assert.match(formatAxisTick(t, 86400 * 30), /2026/)
assert.equal(formatAxisTick(t, 86400 * 365), '2026')
assert.equal(formatLegendTime(t), '30.09.2026, 11:20')
assert.equal(formatLegendTime(null), '–')
})