UI polish across sections: one server selector, skeletons, inline errors
- Every section puts the hub's server selector in the chrome slot, the same place and gap as the Dashboard. - Data regions load with a Skeleton and fail inline with Retry; KPIs show the no-data dash instead of 0 when a server did not answer. Settings keeps its header and selector while loading or failing. - Cache, Apps and Logs headers group secondary or hub-refused actions in menus; app descriptions clamp to two lines; Unknown cells say why. - DNS Client: Protocol label, Resolve and Import together. DHCP and Query Logs empty states with actions. View Logs list capped with scroll. - Blocking: Lists KPI tiles like the Overview, app list table fits its panel with one-line URLs. Status re-reads on tab return at most once a minute. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
172e06f1ce
commit
ba3cdd08d2
43 files changed
+671
-261
No files matched your search
+16
-1
@@ -162,6 +162,16 @@ export function mergeTopResponses(
|
||||
return { topDomains: [] }
|
||||
}
|
||||
|
||||
/* A counter as a number: a number, or a numeric string some servers send. Anything else is not a counter. */
|
||||
function counter(value: unknown): number | null {
|
||||
if (typeof value === 'number') return Number.isFinite(value) ? value : null
|
||||
if (typeof value === 'string' && value.trim() !== '' && Number.isFinite(Number(value))) return Number(value)
|
||||
return null
|
||||
}
|
||||
|
||||
/* What a server holds, as the "Server" panel of the Dashboard counts it. */
|
||||
const HELD = ['zones', 'cachedEntries', 'allowedZones', 'blockedZones', 'allowListZones', 'blockListZones']
|
||||
|
||||
/*
|
||||
`dashboard/stats/get`. Every numeric counter in `stats` is summed. That includes
|
||||
`totalClients` (distinct clients), which over-counts a client seen by both servers:
|
||||
@@ -174,9 +184,14 @@ export function mergeStats(responses: DashboardStats[]): DashboardStats {
|
||||
const stats: Record<string, number> = {}
|
||||
for (const r of responses) {
|
||||
for (const [key, value] of Object.entries(r.stats ?? {})) {
|
||||
if (typeof value === 'number') stats[key] = (stats[key] ?? 0) + value
|
||||
const n = counter(value)
|
||||
if (n !== null) stats[key] = (stats[key] ?? 0) + n
|
||||
}
|
||||
}
|
||||
/* The Dashboard's "Server" panel counts what each server holds (zones, cache, allow and block
|
||||
lists). Those are sums too, and a server that left one out counts as none, so the field is
|
||||
never missing from the total. */
|
||||
for (const key of HELD) stats[key] ??= 0
|
||||
|
||||
const merged: DashboardStats = { ...first, stats }
|
||||
const main = mergeChart(responses.map((r) => r.mainChartData), true)
|
||||
|
||||
Reference in new issue
Block a user