P6-03 repo size trend + agent-update UI fix + dashboard polish #21

Merged
steve merged 17 commits from tidy-up-last-backup-projection into main 2026-05-07 23:00:04 +01:00
4 changed files with 44 additions and 9 deletions
Showing only changes of commit 7011510092 - Show all commits
+1 -1
View File
@@ -284,7 +284,7 @@ func (s *Server) buildRepoTrendView(ctx context.Context, hostID, rangeKey string
chartSVG := sparkline.RenderChart([]sparkline.Series{
{Name: "size", Stroke: "#3b82f6", Axis: sparkline.AxisLeft, Format: sparkline.FormatBytes, Points: sizes},
{Name: "snapshots", Stroke: "#f59e0b", Axis: sparkline.AxisRight, Format: sparkline.FormatCount, Points: counts},
}, dayList, sparkline.ChartOpts{Width: 600, Height: 220})
}, dayList, sparkline.ChartOpts{Width: 640, Height: 220})
return repoTrendView{HostID: hostID, Range: rangeKey, ChartSVG: chartSVG}
}
+41 -6
View File
@@ -156,7 +156,7 @@ func RenderChart(series []Series, days []time.Time, opts ChartOpts) template.HTM
if opts.EmptyLabel == "" {
opts.EmptyLabel = "no data yet"
}
const padL, padR, padT, padB = 56, 56, 16, 28
const padL, padR, padT, padB = 72, 56, 16, 28
w, h := opts.Width, opts.Height
innerW := w - padL - padR
innerH := h - padT - padB
@@ -255,6 +255,11 @@ func RenderChart(series []Series, days []time.Time, opts ChartOpts) template.HTM
continue
}
x := float64(padL) + stepX*float64(i)
if len(days) == 1 {
// Single-day: pin the lone dot to the chart centre so it
// sits under the centred date label.
x = float64(padL) + float64(innerW)/2
}
y := float64(padT) + float64(innerH) - (v-a.min)/(a.max-a.min)*float64(innerH)
fmt.Fprintf(&seg, "%.2f,%.2f ", x, y)
d := days[i]
@@ -267,18 +272,48 @@ func RenderChart(series []Series, days []time.Time, opts ChartOpts) template.HTM
if axArr[AxisLeft].has {
writeAxisLabels(&b, padL-6, padT, innerH, axArr[AxisLeft].min, axArr[AxisLeft].max, FormatBytes, "end")
// Rotated axis title in the left margin. Position inset from
// the viewBox edge by ≈ font-size so the rotated glyph extents
// don't clip against the SVG boundary.
cy := padT + innerH/2
fmt.Fprintf(&b,
`<text x="14" y="%d" text-anchor="middle" font-size="11" fill="currentColor" fill-opacity="0.55" transform="rotate(-90, 14, %d)">Size</text>`,
cy, cy)
}
if axArr[AxisRight].has {
writeAxisLabels(&b, w-padR+6, padT, innerH, axArr[AxisRight].min, axArr[AxisRight].max, FormatCount, "start")
cy := padT + innerH/2
fmt.Fprintf(&b,
`<text x="%d" y="%d" text-anchor="middle" font-size="11" fill="currentColor" fill-opacity="0.55" transform="rotate(90, %d, %d)">Snapshots</text>`,
w-14, cy, w-14, cy)
}
xLabels := []int{0, len(days) / 2, len(days) - 1}
anchors := []string{"start", "middle", "end"}
for i, idx := range xLabels {
x := float64(padL) + stepX*float64(idx)
// X-axis labels at start / mid / end. With 1-2 days the indices
// collapse onto each other — dedupe so we don't stack overlapping
// "Jan 2" labels at the same x coordinate.
type xLabel struct {
idx int
anchor string
}
var xLabels []xLabel
switch {
case len(days) == 1:
xLabels = []xLabel{{0, "middle"}}
case len(days) == 2:
xLabels = []xLabel{{0, "start"}, {1, "end"}}
default:
xLabels = []xLabel{{0, "start"}, {len(days) / 2, "middle"}, {len(days) - 1, "end"}}
}
for _, l := range xLabels {
x := float64(padL) + stepX*float64(l.idx)
// With a single point, anchor "middle" centres on padL — push to
// the chart's centre line so the lone label sits over the dot.
if len(days) == 1 {
x = float64(padL) + float64(innerW)/2
}
fmt.Fprintf(&b,
`<text x="%.2f" y="%d" text-anchor="%s" font-size="10" fill="currentColor" fill-opacity="0.55">%s</text>`,
x, h-padB+16, anchors[i], days[idx].Format("Jan 2"))
x, h-padB+16, l.anchor, days[l.idx].Format("Jan 2"))
}
b.WriteString(`</svg>`)
+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 220" class="repo-trend-chart" role="img" aria-label="repo size and snapshot count over time"><line x1="56" y1="110" x2="544" y2="110" stroke="currentColor" stroke-opacity="0.15" stroke-dasharray="3,3"/><text x="300" y="114" text-anchor="middle" font-size="12" fill="currentColor" fill-opacity="0.4">no data yet</text></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 220" class="repo-trend-chart" role="img" aria-label="repo size and snapshot count over time"><line x1="72" y1="110" x2="544" y2="110" stroke="currentColor" stroke-opacity="0.15" stroke-dasharray="3,3"/><text x="300" y="114" text-anchor="middle" font-size="12" fill="currentColor" fill-opacity="0.4">no data yet</text></svg>

Before

Width:  |  Height:  |  Size: 381 B

After

Width:  |  Height:  |  Size: 381 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 220" class="repo-trend-chart" role="img" aria-label="repo size and snapshot count over time"><line x1="56" y1="16" x2="544" y2="16" stroke="currentColor" stroke-opacity="0.08"/><line x1="56" y1="60" x2="544" y2="60" stroke="currentColor" stroke-opacity="0.08"/><line x1="56" y1="104" x2="544" y2="104" stroke="currentColor" stroke-opacity="0.08"/><line x1="56" y1="148" x2="544" y2="148" stroke="currentColor" stroke-opacity="0.08"/><line x1="56" y1="192" x2="544" y2="192" stroke="currentColor" stroke-opacity="0.08"/><circle cx="56.00" cy="192.00" r="2.5" fill="#3b82f6"><title>2026-05-01 · size: 1.0 KiB</title></circle><circle cx="218.67" cy="166.86" r="2.5" fill="#3b82f6"><title>2026-05-02 · size: 2.0 KiB</title></circle><circle cx="381.33" cy="116.57" r="2.5" fill="#3b82f6"><title>2026-05-03 · size: 4.0 KiB</title></circle><circle cx="544.00" cy="16.00" r="2.5" fill="#3b82f6"><title>2026-05-04 · size: 8.0 KiB</title></circle><polyline fill="none" stroke="#3b82f6" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" points="56.00,192.00 218.67,166.86 381.33,116.57 544.00,16.00"/><circle cx="56.00" cy="192.00" r="2.5" fill="#f59e0b"><title>2026-05-01 · snapshots: 1</title></circle><circle cx="218.67" cy="133.33" r="2.5" fill="#f59e0b"><title>2026-05-02 · snapshots: 2</title></circle><circle cx="381.33" cy="74.67" r="2.5" fill="#f59e0b"><title>2026-05-03 · snapshots: 3</title></circle><circle cx="544.00" cy="16.00" r="2.5" fill="#f59e0b"><title>2026-05-04 · snapshots: 4</title></circle><polyline fill="none" stroke="#f59e0b" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" points="56.00,192.00 218.67,133.33 381.33,74.67 544.00,16.00"/><text x="50" y="19" text-anchor="end" font-size="10" fill="currentColor" fill-opacity="0.55">8.0 KiB</text><text x="50" y="63" text-anchor="end" font-size="10" fill="currentColor" fill-opacity="0.55">6.2 KiB</text><text x="50" y="107" text-anchor="end" font-size="10" fill="currentColor" fill-opacity="0.55">4.5 KiB</text><text x="50" y="151" text-anchor="end" font-size="10" fill="currentColor" fill-opacity="0.55">2.8 KiB</text><text x="50" y="195" text-anchor="end" font-size="10" fill="currentColor" fill-opacity="0.55">1.0 KiB</text><text x="550" y="19" text-anchor="start" font-size="10" fill="currentColor" fill-opacity="0.55">4</text><text x="550" y="63" text-anchor="start" font-size="10" fill="currentColor" fill-opacity="0.55">3</text><text x="550" y="107" text-anchor="start" font-size="10" fill="currentColor" fill-opacity="0.55">2</text><text x="550" y="151" text-anchor="start" font-size="10" fill="currentColor" fill-opacity="0.55">2</text><text x="550" y="195" text-anchor="start" font-size="10" fill="currentColor" fill-opacity="0.55">1</text><text x="56.00" y="208" text-anchor="start" font-size="10" fill="currentColor" fill-opacity="0.55">May 1</text><text x="381.33" y="208" text-anchor="middle" font-size="10" fill="currentColor" fill-opacity="0.55">May 3</text><text x="544.00" y="208" text-anchor="end" font-size="10" fill="currentColor" fill-opacity="0.55">May 4</text></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 220" class="repo-trend-chart" role="img" aria-label="repo size and snapshot count over time"><line x1="72" y1="16" x2="544" y2="16" stroke="currentColor" stroke-opacity="0.08"/><line x1="72" y1="60" x2="544" y2="60" stroke="currentColor" stroke-opacity="0.08"/><line x1="72" y1="104" x2="544" y2="104" stroke="currentColor" stroke-opacity="0.08"/><line x1="72" y1="148" x2="544" y2="148" stroke="currentColor" stroke-opacity="0.08"/><line x1="72" y1="192" x2="544" y2="192" stroke="currentColor" stroke-opacity="0.08"/><circle cx="72.00" cy="192.00" r="2.5" fill="#3b82f6"><title>2026-05-01 · size: 1.0 KiB</title></circle><circle cx="229.33" cy="166.86" r="2.5" fill="#3b82f6"><title>2026-05-02 · size: 2.0 KiB</title></circle><circle cx="386.67" cy="116.57" r="2.5" fill="#3b82f6"><title>2026-05-03 · size: 4.0 KiB</title></circle><circle cx="544.00" cy="16.00" r="2.5" fill="#3b82f6"><title>2026-05-04 · size: 8.0 KiB</title></circle><polyline fill="none" stroke="#3b82f6" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" points="72.00,192.00 229.33,166.86 386.67,116.57 544.00,16.00"/><circle cx="72.00" cy="192.00" r="2.5" fill="#f59e0b"><title>2026-05-01 · snapshots: 1</title></circle><circle cx="229.33" cy="133.33" r="2.5" fill="#f59e0b"><title>2026-05-02 · snapshots: 2</title></circle><circle cx="386.67" cy="74.67" r="2.5" fill="#f59e0b"><title>2026-05-03 · snapshots: 3</title></circle><circle cx="544.00" cy="16.00" r="2.5" fill="#f59e0b"><title>2026-05-04 · snapshots: 4</title></circle><polyline fill="none" stroke="#f59e0b" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" points="72.00,192.00 229.33,133.33 386.67,74.67 544.00,16.00"/><text x="66" y="19" text-anchor="end" font-size="10" fill="currentColor" fill-opacity="0.55">8.0 KiB</text><text x="66" y="63" text-anchor="end" font-size="10" fill="currentColor" fill-opacity="0.55">6.2 KiB</text><text x="66" y="107" text-anchor="end" font-size="10" fill="currentColor" fill-opacity="0.55">4.5 KiB</text><text x="66" y="151" text-anchor="end" font-size="10" fill="currentColor" fill-opacity="0.55">2.8 KiB</text><text x="66" y="195" text-anchor="end" font-size="10" fill="currentColor" fill-opacity="0.55">1.0 KiB</text><text x="14" y="104" text-anchor="middle" font-size="11" fill="currentColor" fill-opacity="0.55" transform="rotate(-90, 14, 104)">Size</text><text x="550" y="19" text-anchor="start" font-size="10" fill="currentColor" fill-opacity="0.55">4</text><text x="550" y="63" text-anchor="start" font-size="10" fill="currentColor" fill-opacity="0.55">3</text><text x="550" y="107" text-anchor="start" font-size="10" fill="currentColor" fill-opacity="0.55">2</text><text x="550" y="151" text-anchor="start" font-size="10" fill="currentColor" fill-opacity="0.55">2</text><text x="550" y="195" text-anchor="start" font-size="10" fill="currentColor" fill-opacity="0.55">1</text><text x="586" y="104" text-anchor="middle" font-size="11" fill="currentColor" fill-opacity="0.55" transform="rotate(90, 586, 104)">Snapshots</text><text x="72.00" y="208" text-anchor="start" font-size="10" fill="currentColor" fill-opacity="0.55">May 1</text><text x="386.67" y="208" text-anchor="middle" font-size="10" fill="currentColor" fill-opacity="0.55">May 3</text><text x="544.00" y="208" text-anchor="end" font-size="10" fill="currentColor" fill-opacity="0.55">May 4</text></svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB