Serverpuls: JWT-Cache, Lade-Zustand beim Umschalten, Antwort-Reihenfolge

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 ec740ca970
commit 16f3150ff1
4 files changed
+116 -8

No files matched your search

+16 -6
View File
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useCallback, useEffect, useRef, useMemo, useState } from 'react'
import { Navigate } from 'react-router-dom'
import { useAuth } from '../context/AuthContext'
import { PageHeader } from '../components/ui'
@@ -50,7 +50,7 @@ function displayPoints(metric, points, total) {
function tileModel(key, m) {
if (!m) return null
const total = m.total ?? null
const spark = (m.spark24h || []).map((p) => (key === 'ram' && total ? total - p.v : p.v))
const spark = (m.spark24h || []).map((p) => (key === 'ram' && total && p.v !== null && p.v !== undefined ? total - p.v : p.v))
if (m.value === null || m.value === undefined) return { main: '–', sub: '', spark, delta: null }
switch (key) {
case 'cpu':
@@ -159,16 +159,21 @@ export default function ServerpulsPage() {
const [tick, setTick] = useState(0)
// Uebersicht, Projekte, Aufraeumbar: beim Laden und jede Minute (nur sichtbar)
const reqId = useRef(0)
const loadMain = useCallback(async () => {
reqId.current += 1
const mine = reqId.current
try {
const [o, a, c] = await Promise.all([
serverpulsApi.overview(), serverpulsApi.apps(), serverpulsApi.cleanup(),
])
if (mine !== reqId.current) return
setOverview(o)
setAppsData(a)
setCleanup(c)
setError('')
} catch (e) {
if (mine !== reqId.current) return
setError(e.message || 'Serverpuls ist nicht erreichbar.')
}
setNow(Date.now())
@@ -206,6 +211,11 @@ export default function ServerpulsPage() {
return () => { dead = true }
}, [isAdmin, selected, detailRange, tick])
const changeMetric = (v) => { if (v !== metric) { setChart(null); setMetric(v) } }
const changeRange = (v) => { if (v !== range) { setChart(null); setRange(v) } }
const selectApp = (id) => { setDetail(null); setSelected(id) }
const changeDetailRange = (v) => { if (v !== detailRange) { setDetail(null); setDetailRange(v) } }
const m = METRICS.find((x) => x.id === metric)
const chartData = useMemo(() => {
@@ -286,8 +296,8 @@ export default function ServerpulsPage() {
<div className="sp-panel-head">
<h3>Verlauf</h3>
<div className="sp-controls">
<Pills items={METRICS} value={metric} onChange={setMetric} testid="sp-metric-select" />
<Pills items={RANGES} value={range} onChange={setRange} testid="sp-range-select" />
<Pills items={METRICS} value={metric} onChange={changeMetric} testid="sp-metric-select" />
<Pills items={RANGES} value={range} onChange={changeRange} testid="sp-range-select" />
</div>
</div>
{chartData && chartData.data[0].length > 0
@@ -300,12 +310,12 @@ export default function ServerpulsPage() {
<h3>Projekte</h3>
{appsData?.sizesDay && <span className="sp-muted sp-small">Größen von {appsData.sizesDay}</span>}
</div>
<AppsTable rows={appsData?.rows || []} selectedId={selected} onSelect={setSelected} />
<AppsTable rows={appsData?.rows || []} selectedId={selected} onSelect={selectApp} />
{selected && (
<div className="sp-detail" data-testid="sp-detail">
<div className="sp-panel-head">
<h3>{detail?.name || selected}</h3>
<Pills items={RANGES} value={detailRange} onChange={setDetailRange} testid="sp-detail-range" />
<Pills items={RANGES} value={detailRange} onChange={changeDetailRange} testid="sp-detail-range" />
</div>
{detailData ? (
<div className="sp-detail-charts">