ui(hosts): declutter — per-cluster panels, stacked driver identity, abbreviated Last error, Controls in a dialog

The page ran members, the driver-instances heading, and every cluster group
together inside one panel; driver name and id slammed inline; one long driver
exception blew the table out sideways (Controls scrolled out of reach); and the
inline #546 expander row dumped a full-width panel into the table.

- One panel per cluster group, node chips in the panel head.
- Driver id renders on its own muted mono line under the name.
- Last error abbreviated to 60 chars (DisplayText.Abbreviate — #504 rule),
  full text on the tooltip and in the Controls dialog.
- DriverStatusPanel moved from an inline expander row to a modal (one at a
  time); the dialog re-resolves its row from the current groups each render so
  the 5 s rebuild can't strand it, and the Enabled-in-key recycle discipline
  carries over unchanged. HostsPanelWiringTests re-pinned to the dialog markup.

Live-verified on docker-dev: separated cards, in-viewport table, dialog opens
with live breaker/status data and grouped Reconnect|Restart, closes cleanly.
This commit is contained in:
Joseph Doherty
2026-08-10 17:34:37 -04:00
parent ef55ff3977
commit 34d60c4125
2 changed files with 103 additions and 76 deletions
@@ -43,17 +43,20 @@ public sealed class HostsPanelWiringTests
}
/// <summary>
/// The panel must be fed the row's real identity and enabled-state, not literals. Hard-coding
/// any of the three would make every expanded row drive the same driver, or claim a disabled
/// instance is live — both of which a source scan for the tag alone would pass.
/// The panel must be fed the dialog driver's real identity and enabled-state, not literals.
/// Hard-coding any of the three would make every dialog drive the same driver, or claim a
/// disabled instance is live — both of which a source scan for the tag alone would pass.
/// Since the Controls dialog rework the panel renders once, inside the modal, bound to the
/// row that <c>FindControlsDriver()</c> re-resolves from the current groups (<c>cd</c>/<c>cg</c>)
/// — never to a cached row record, which the 5 s rebuild replaces.
/// </summary>
[Fact]
public void The_panel_is_bound_to_the_rows_own_driver_cluster_and_enabled_state()
public void The_panel_is_bound_to_the_dialogs_own_driver_cluster_and_enabled_state()
{
var source = ReadHostsPageSource();
source.ShouldContain("DriverInstanceId=\"@d.DriverInstanceId\"");
source.ShouldContain("ClusterId=\"@g.ClusterId\"");
source.ShouldContain("Enabled=\"@d.Enabled\"");
source.ShouldContain("DriverInstanceId=\"@cd.DriverInstanceId\"");
source.ShouldContain("ClusterId=\"@cg.ClusterId\"");
source.ShouldContain("Enabled=\"@cd.Enabled\"");
}
}