Serverpuls: deutsche Zeitachse und Legende im Diagramm
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
16f3150ff1
commit
57f235dd36
3 files changed
+60
-3
No files matched your search
@@ -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',
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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), '–')
|
||||
})
|
||||
Reference in new issue
Block a user