mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-06-09 18:32:43 +00: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:
@@ -23,20 +23,25 @@ type dashSample struct {
|
||||
workers float64
|
||||
}
|
||||
|
||||
// TrendCard is one at-a-glance metric rendered on the dashboard: a current
|
||||
// value plus an inline SVG sparkline of its recent history.
|
||||
type TrendCard struct {
|
||||
Title string
|
||||
Value string
|
||||
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.
|
||||
// DashboardTrends carries inline-SVG sparklines of recent cluster history,
|
||||
// keyed to the dashboard's existing summary cards so each card shows a value
|
||||
// plus its trend (rather than a separate, duplicate row). Maintenance metrics
|
||||
// that have no existing card carry their current value too, and fill the
|
||||
// previously-empty columns of the EC row.
|
||||
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
|
||||
@@ -94,22 +99,21 @@ func (s *AdminServer) GetDashboardTrends() DashboardTrends {
|
||||
}
|
||||
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 })
|
||||
workers := series(func(s dashSample) float64 { return s.workers })
|
||||
|
||||
cards := []TrendCard{
|
||||
{Title: "Volumes", Value: trendCount(last(vol)), Icon: "fa-database", Color: "primary", SparkSVG: sparklineSVG(vol, "#4e73df")},
|
||||
{Title: "EC Shards", Value: trendCount(last(ec)), Icon: "fa-th-large", Color: "info", SparkSVG: sparklineSVG(ec, "#36b9cc")},
|
||||
{Title: "Disk Used", Value: trendBytes(last(disk)), Icon: "fa-hdd", Color: "success", SparkSVG: sparklineSVG(disk, "#1cc88a")},
|
||||
{Title: "Files", Value: trendCount(last(files)), Icon: "fa-file", Color: "warning", SparkSVG: sparklineSVG(files, "#f6c23e")},
|
||||
{Title: "Active Tasks", Value: trendCount(last(tasks)), Icon: "fa-tasks", Color: "secondary", SparkSVG: sparklineSVG(tasks, "#858796")},
|
||||
{Title: "Workers", Value: trendCount(last(workers)), Icon: "fa-users-cog", Color: "dark", SparkSVG: sparklineSVG(workers, "#5a5c69")},
|
||||
// Sparkline colors match the existing cards' border colors.
|
||||
return DashboardTrends{
|
||||
Samples: len(samples),
|
||||
Volumes: sparklineSVG(series(func(s dashSample) float64 { return s.volumes }), "#1cc88a"), // success
|
||||
Files: sparklineSVG(series(func(s dashSample) float64 { return s.files }), "#36b9cc"), // info
|
||||
DiskUsed: sparklineSVG(series(func(s dashSample) float64 { return s.diskUsed }), "#f6c23e"), // warning
|
||||
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 {
|
||||
@@ -173,20 +177,3 @@ func trendCount(v float64) string {
|
||||
}
|
||||
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 id="dashboard-content">
|
||||
<!-- Cluster Trends: native at-a-glance sparklines from the admin's own
|
||||
recent 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 -->
|
||||
<!-- Summary Cards — each carries an inline sparkline of recent history
|
||||
from the admin's own snapshots (no Prometheus required). -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@templ.Raw(data.Trends.Volumes)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -84,6 +61,7 @@ templ Admin(data dash.AdminData) {
|
||||
<i class="fas fa-file fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
@templ.Raw(data.Trends.Files)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -104,6 +82,7 @@ templ Admin(data dash.AdminData) {
|
||||
<i class="fas fa-hdd fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
@templ.Raw(data.Trends.DiskUsed)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -167,13 +146,52 @@ templ Admin(data dash.AdminData) {
|
||||
<i class="fas fa-puzzle-piece fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
@templ.Raw(data.Trends.EcShards)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Empty columns to balance the row -->
|
||||
<div class="col-xl-3 col-md-6 mb-4"></div>
|
||||
<div class="col-xl-3 col-md-6 mb-4"></div>
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-info 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-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>
|
||||
|
||||
<!-- Master Nodes Status -->
|
||||
|
||||
+374
-401
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user