mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-15 18:48:32 +02:00
admin: fold dashboard sparklines into the existing cards (de-dup) (#9964)
admin: fold dashboard sparklines into the existing cards The trend sparklines added in #9957 lived in a separate "Cluster Trends" row that duplicated the existing summary cards (Volumes, Files, Disk Used, EC Shards). Remove that row and instead render each sparkline inside the matching summary card, so every headline number shows its recent trend without duplication. The two maintenance metrics that have no existing card — Active Tasks and Workers — now fill the previously-empty columns of the EC row (also with sparklines). DashboardTrends changes from a Cards slice to named per-card sparkline SVGs (+ current values for the two maintenance cards). Drops the now-unused trendBytes helper (disk size keeps using the existing formatBytes).
This commit is contained in:
1 parent
b13463880c
commit
d47cc45b1f
3 files changed
+450
-472
No files matched your search
@@ -23,20 +23,25 @@ type dashSample struct {
|
|||||||
workers float64
|
workers float64
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrendCard is one at-a-glance metric rendered on the dashboard: a current
|
// DashboardTrends carries inline-SVG sparklines of recent cluster history,
|
||||||
// value plus an inline SVG sparkline of its recent history.
|
// keyed to the dashboard's existing summary cards so each card shows a value
|
||||||
type TrendCard struct {
|
// plus its trend (rather than a separate, duplicate row). Maintenance metrics
|
||||||
Title string
|
// that have no existing card carry their current value too, and fill the
|
||||||
Value string
|
// previously-empty columns of the EC row.
|
||||||
Icon string // FontAwesome icon class, e.g. "fa-database"
|
|
||||||
Color string // Bootstrap contextual color, e.g. "primary"
|
|
||||||
SparkSVG string // self-contained inline <svg>…</svg>
|
|
||||||
}
|
|
||||||
|
|
||||||
// DashboardTrends is the set of trend cards shown on the dashboard.
|
|
||||||
type DashboardTrends struct {
|
type DashboardTrends struct {
|
||||||
Cards []TrendCard `json:"-"`
|
Samples int `json:"samples"`
|
||||||
Samples int `json:"samples"`
|
|
||||||
|
// Sparklines (raw <svg>) for the existing summary cards.
|
||||||
|
Volumes string `json:"-"`
|
||||||
|
Files string `json:"-"`
|
||||||
|
DiskUsed string `json:"-"`
|
||||||
|
EcShards string `json:"-"`
|
||||||
|
|
||||||
|
// Maintenance cards: value + sparkline.
|
||||||
|
Tasks string `json:"-"`
|
||||||
|
TasksValue string `json:"tasks"`
|
||||||
|
Workers string `json:"-"`
|
||||||
|
WorkersValue string `json:"workers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// recordDashboardSample snapshots headline cluster numbers into the ring
|
// recordDashboardSample snapshots headline cluster numbers into the ring
|
||||||
@@ -94,22 +99,21 @@ func (s *AdminServer) GetDashboardTrends() DashboardTrends {
|
|||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
vol := series(func(s dashSample) float64 { return s.volumes })
|
|
||||||
ec := series(func(s dashSample) float64 { return s.ecShards })
|
|
||||||
disk := series(func(s dashSample) float64 { return s.diskUsed })
|
|
||||||
files := series(func(s dashSample) float64 { return s.files })
|
|
||||||
tasks := series(func(s dashSample) float64 { return s.tasks })
|
tasks := series(func(s dashSample) float64 { return s.tasks })
|
||||||
workers := series(func(s dashSample) float64 { return s.workers })
|
workers := series(func(s dashSample) float64 { return s.workers })
|
||||||
|
|
||||||
cards := []TrendCard{
|
// Sparkline colors match the existing cards' border colors.
|
||||||
{Title: "Volumes", Value: trendCount(last(vol)), Icon: "fa-database", Color: "primary", SparkSVG: sparklineSVG(vol, "#4e73df")},
|
return DashboardTrends{
|
||||||
{Title: "EC Shards", Value: trendCount(last(ec)), Icon: "fa-th-large", Color: "info", SparkSVG: sparklineSVG(ec, "#36b9cc")},
|
Samples: len(samples),
|
||||||
{Title: "Disk Used", Value: trendBytes(last(disk)), Icon: "fa-hdd", Color: "success", SparkSVG: sparklineSVG(disk, "#1cc88a")},
|
Volumes: sparklineSVG(series(func(s dashSample) float64 { return s.volumes }), "#1cc88a"), // success
|
||||||
{Title: "Files", Value: trendCount(last(files)), Icon: "fa-file", Color: "warning", SparkSVG: sparklineSVG(files, "#f6c23e")},
|
Files: sparklineSVG(series(func(s dashSample) float64 { return s.files }), "#36b9cc"), // info
|
||||||
{Title: "Active Tasks", Value: trendCount(last(tasks)), Icon: "fa-tasks", Color: "secondary", SparkSVG: sparklineSVG(tasks, "#858796")},
|
DiskUsed: sparklineSVG(series(func(s dashSample) float64 { return s.diskUsed }), "#f6c23e"), // warning
|
||||||
{Title: "Workers", Value: trendCount(last(workers)), Icon: "fa-users-cog", Color: "dark", SparkSVG: sparklineSVG(workers, "#5a5c69")},
|
EcShards: sparklineSVG(series(func(s dashSample) float64 { return s.ecShards }), "#5a5c69"), // dark
|
||||||
|
Tasks: sparklineSVG(tasks, "#36b9cc"),
|
||||||
|
TasksValue: trendCount(last(tasks)),
|
||||||
|
Workers: sparklineSVG(workers, "#4e73df"),
|
||||||
|
WorkersValue: trendCount(last(workers)),
|
||||||
}
|
}
|
||||||
return DashboardTrends{Cards: cards, Samples: len(samples)}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func last(v []float64) float64 {
|
func last(v []float64) float64 {
|
||||||
@@ -173,20 +177,3 @@ func trendCount(v float64) string {
|
|||||||
}
|
}
|
||||||
return string(out)
|
return string(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// trendBytes formats a byte count as a human-readable size.
|
|
||||||
func trendBytes(v float64) string {
|
|
||||||
const unit = 1024.0
|
|
||||||
if v < unit {
|
|
||||||
return fmt.Sprintf("%.0f B", v)
|
|
||||||
}
|
|
||||||
const units = "KMGTPE"
|
|
||||||
div, exp := unit, 0
|
|
||||||
// Cap exp at the last unit so an anomalously huge value can't index past
|
|
||||||
// the units string and panic.
|
|
||||||
for n := v / unit; n >= unit && exp < len(units)-1; n /= unit {
|
|
||||||
div *= unit
|
|
||||||
exp++
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%.1f %ciB", v/div, units[exp])
|
|
||||||
}
|
|
||||||
@@ -21,32 +21,8 @@ templ Admin(data dash.AdminData) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="dashboard-content">
|
<div id="dashboard-content">
|
||||||
<!-- Cluster Trends: native at-a-glance sparklines from the admin's own
|
<!-- Summary Cards — each carries an inline sparkline of recent history
|
||||||
recent snapshots (no Prometheus required). -->
|
from the admin's own snapshots (no Prometheus required). -->
|
||||||
<div class="d-flex align-items-center mb-2">
|
|
||||||
<h6 class="text-uppercase text-muted small mb-0">
|
|
||||||
<i class="fas fa-chart-line me-1"></i>Cluster Trends
|
|
||||||
</h6>
|
|
||||||
<span class="text-muted small ms-2">({ fmt.Sprintf("%d", data.Trends.Samples) } recent samples)</span>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-4">
|
|
||||||
for _, c := range data.Trends.Cards {
|
|
||||||
<div class="col-xl-2 col-md-4 col-6 mb-3">
|
|
||||||
<div class="card shadow h-100 py-2">
|
|
||||||
<div class="card-body py-2 px-3">
|
|
||||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
|
||||||
<span class={ fmt.Sprintf("text-xs font-weight-bold text-uppercase text-%s", c.Color) }>{ c.Title }</span>
|
|
||||||
<i class={ fmt.Sprintf("fas %s text-gray-300", c.Icon) }></i>
|
|
||||||
</div>
|
|
||||||
<div class="h5 mb-1 font-weight-bold text-gray-800">{ c.Value }</div>
|
|
||||||
@templ.Raw(c.SparkSVG)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Summary Cards -->
|
|
||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
<div class="col-xl-3 col-md-6 mb-4">
|
<div class="col-xl-3 col-md-6 mb-4">
|
||||||
<div class="card border-left-success shadow h-100 py-2">
|
<div class="card border-left-success shadow h-100 py-2">
|
||||||
@@ -64,6 +40,7 @@ templ Admin(data dash.AdminData) {
|
|||||||
<i class="fas fa-database fa-2x text-gray-300"></i>
|
<i class="fas fa-database fa-2x text-gray-300"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@templ.Raw(data.Trends.Volumes)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -84,6 +61,7 @@ templ Admin(data dash.AdminData) {
|
|||||||
<i class="fas fa-file fa-2x text-gray-300"></i>
|
<i class="fas fa-file fa-2x text-gray-300"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@templ.Raw(data.Trends.Files)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -104,6 +82,7 @@ templ Admin(data dash.AdminData) {
|
|||||||
<i class="fas fa-hdd fa-2x text-gray-300"></i>
|
<i class="fas fa-hdd fa-2x text-gray-300"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@templ.Raw(data.Trends.DiskUsed)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -167,13 +146,52 @@ templ Admin(data dash.AdminData) {
|
|||||||
<i class="fas fa-puzzle-piece fa-2x text-gray-300"></i>
|
<i class="fas fa-puzzle-piece fa-2x text-gray-300"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@templ.Raw(data.Trends.EcShards)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Empty columns to balance the row -->
|
<div class="col-xl-3 col-md-6 mb-4">
|
||||||
<div class="col-xl-3 col-md-6 mb-4"></div>
|
<div class="card border-left-info shadow h-100 py-2">
|
||||||
<div class="col-xl-3 col-md-6 mb-4"></div>
|
<div class="card-body">
|
||||||
|
<div class="row no-gutters align-items-center">
|
||||||
|
<div class="col mr-2">
|
||||||
|
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">
|
||||||
|
Active Tasks
|
||||||
|
</div>
|
||||||
|
<div class="h5 mb-0 font-weight-bold text-gray-800">
|
||||||
|
{ data.Trends.TasksValue }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<i class="fas fa-tasks fa-2x text-gray-300"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@templ.Raw(data.Trends.Tasks)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-xl-3 col-md-6 mb-4">
|
||||||
|
<div class="card border-left-primary shadow h-100 py-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row no-gutters align-items-center">
|
||||||
|
<div class="col mr-2">
|
||||||
|
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">
|
||||||
|
Workers
|
||||||
|
</div>
|
||||||
|
<div class="h5 mb-0 font-weight-bold text-gray-800">
|
||||||
|
{ data.Trends.WorkersValue }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<i class="fas fa-users-cog fa-2x text-gray-300"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@templ.Raw(data.Trends.Workers)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Master Nodes Status -->
|
<!-- Master Nodes Status -->
|
||||||
|
|||||||
+374
-401
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user