feat(webui): break the topology PSU row into one card per supply
Each PSU is its own card, coloured by its own status — a failed unit goes red on its own instead of dragging a single grouped card down — and shows input voltage + draw (measured output/input, else nameplate rating). Cards click through to the PSU detail modal and carry data-psu so the shared topoLiveScript refreshes their wattage from /api/metrics/latest on the same 5s poll as the fan tiles. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019VHG21rgTUiR1G3qFHTVmN
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
a4853a0a4f
commit
59271fa674
@@ -90,15 +90,22 @@ func TestTopoPageRendersArbitraryPSUAndFirmwareCountsAsFlexRows(t *testing.T) {
|
||||
path := filepath.Join(dir, "audit.json")
|
||||
|
||||
okStatus := "OK"
|
||||
failStatus := "Critical"
|
||||
watt := 3000
|
||||
volt := 230.0
|
||||
|
||||
var psus []schema.HardwarePowerSupply
|
||||
for i := 0; i < 6; i++ {
|
||||
slot := strconv.Itoa(i)
|
||||
st := okStatus
|
||||
if i == 3 {
|
||||
st = failStatus
|
||||
}
|
||||
psus = append(psus, schema.HardwarePowerSupply{
|
||||
HardwareComponentStatus: schema.HardwareComponentStatus{Status: &okStatus},
|
||||
HardwareComponentStatus: schema.HardwareComponentStatus{Status: &st},
|
||||
Slot: &slot,
|
||||
WattageW: &watt,
|
||||
InputVoltage: &volt,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -132,14 +139,20 @@ func TestTopoPageRendersArbitraryPSUAndFirmwareCountsAsFlexRows(t *testing.T) {
|
||||
if !strings.Contains(body, "BIOS") || !strings.Contains(body, "BMC") {
|
||||
t.Fatalf("topo page missing BIOS/BMC firmware boxes: %s", body)
|
||||
}
|
||||
// All 6 PSUs must be represented, grouped into one stacked card with a
|
||||
// count rather than 6 separate boxes.
|
||||
if !strings.Contains(body, "Power Supplies ×6") {
|
||||
t.Fatalf("topo page missing grouped Power Supplies x6 card: %s", body)
|
||||
// One card per PSU (6), each clickable, each showing voltage + power.
|
||||
if n := strings.Count(body, `class="topo-psu-tile"`); n != 6 {
|
||||
t.Fatalf("expected 6 per-PSU cards, got %d", n)
|
||||
}
|
||||
if strings.Count(body, `onclick="openComponentDetail('psu')"`) != 1 &&
|
||||
strings.Count(body, `onclick="openComponentDetail('psu')"`) != 1 {
|
||||
t.Fatalf("expected exactly one clickable PSU group card, not one per PSU: %s", body)
|
||||
if n := strings.Count(body, `onclick="openComponentDetail('psu')"`) +
|
||||
strings.Count(body, `onclick="openComponentDetail('psu')"`); n != 6 {
|
||||
t.Fatalf("expected one clickable card per PSU (6), got %d", n)
|
||||
}
|
||||
if !strings.Contains(body, "230 V · 3000 W rated") {
|
||||
t.Fatalf("PSU card missing voltage/power line: %s", body)
|
||||
}
|
||||
// The one failed PSU is coloured red on its own (crit token) and labelled.
|
||||
if !strings.Contains(body, "var(--crit-bg)") || !strings.Contains(body, "CRITICAL") {
|
||||
t.Fatalf("failed PSU should render individually as critical: %s", body)
|
||||
}
|
||||
// Firmware/PSU rows must be flex-wrap HTML (arbitrary count, no overlap),
|
||||
// not absolutely-positioned SVG rects sharing fixed x/y coordinates.
|
||||
|
||||
Reference in New Issue
Block a user