diff --git a/admin_ui_cleanup.md b/admin_ui_cleanup.md index 9e95f1a..9c16e92 100644 --- a/admin_ui_cleanup.md +++ b/admin_ui_cleanup.md @@ -15,7 +15,7 @@ always a live browser pass.** --- -## 1. Discovery — four parallel read-only scans +## 1. Discovery — five read-only scans (1a–1d parallelize per file; 1d also needs a live walk) Run these as parallel read-only agents (or by hand) and write the inventories down with `file:line` before changing anything. The inventories become the per-batch fix briefs. @@ -84,7 +84,27 @@ Inventory, with file:line: code's `SectionName` said `Security:Ldap` — always verify cited key names against the options class). Wrong facts outrank style: fix or delete them even if you skip everything else. -### 1d. Tree tables — check the data shape before porting the component +### 1d. Density / layout scan (found post-sweep, on OtOpcUa's `/hosts`) + +The four scans above can all pass while a page is still unreadable — the sweep left `/hosts` +structurally correct and visually overloaded ("text slams together, the table is overloaded, no +separation between sections"). Walk each *page* (not each file) with realistic data and look for: + +- **Sections running together.** One `panel` wrapping several groups, each with its own nested + `panel-head` — three headings stack flush with no card boundaries. Tell: `panel-head` appearing + more than once inside one `
`. +- **Identity slam.** A cell rendering name + id + status chips inline in one run + (`gate-modbus-renamedDRV-ce2fb5217f5d`). Two identifiers in one cell need two lines. +- **Unbounded free-text columns.** Any column bound to text the app doesn't control — error + messages, exception text, paths, config values — with no truncation. One long driver exception + widened the table until the actions column scrolled out of reach. Tell: `@d.SomeError`, + `@x.Message` etc. rendered bare in a ``. +- **Inline full-width expander/detail rows** inside an already-dense table (a `colspan` row hosting + a whole panel). They destroy scannability exactly when the operator most needs it. +- **Realistic data matters.** On a healthy rig every error cell reads "—" and the defect is + invisible — the live gate below must be run with at least one faulted/erroring row on screen. + +### 1e. Tree tables — check the data shape before porting the component The router built a generic `TreeTable` + `TreeVisibility` (depth-ordered flat `ITreeRow` list, forward-scan visibility — reference implementation in @@ -154,6 +174,28 @@ foundation branch merges — all branches build independently and compose at mer - Respect existing "buttons-not-anchors" comments (Blazor's `blazor.web.js` navigation interceptor is why some tree labels are buttons) — never convert those to links. +### Density fixes (the `/hosts` rework, applicable anywhere 1d fires) + +- **One panel per group.** Replace panel-wrapping-groups with one `section.panel` per group; hoist + the group's metadata (node/host chips, counts) into that panel's head so it stops being a + paragraph between heading and table. +- **Stack identities.** Primary name on the first line; the machine id on its own + `
` beneath. Chips stay with the name line. +- **Truncate unbounded text with a safe helper, never a raw slice.** OtOpcUa's + `DisplayText.Abbreviate(value, 60)` + the full text on `title` — a raw `@x[..60]` 500s the whole + page when the value is shorter (OtOpcUa #504). Keep the full text reachable (tooltip + the + row's detail dialog). +- **Inline expander rows → a dialog.** Host the same detail component in a Bootstrap modal + (`modal-backdrop fade show` + `modal fade show d-block`, `modal-lg`), one at a time. Lifecycle + cautions that made the OtOpcUa version correct: + - Hold only the **id** of the open row on the page component; **re-resolve the row from current + data every render** — auto-refresh replaces row records, and a dialog caching one goes stale or + strands. A row that disappears ⇒ the dialog renders nothing (self-closes). + - Preserve any `@key` recycle discipline the inline version carried (OtOpcUa keys the panel on + `id + Enabled` so a config flip disposes and remounts it, keeping store subscriptions honest). + - Expect **source-scan guard tests** to pin the old markup (no bUnit ⇒ repos guard wiring by + grepping the `.razor` source); re-pin them to the dialog markup in the same commit. + ### Execution pattern (what actually worked, twice) - Discovery scans → plan doc with the full inventories → **one foundation agent + N page-batch @@ -182,6 +224,10 @@ you the stale one otherwise), then walk: write on shared rigs); verify disarm. 5. Anything the scoped bundle resurrected (context menus float at the cursor, backdrops cover). 6. Legends/hints/empty states from the KEEP list still render. +7. **Density, with realistic data**: at least one faulted/erroring row on screen (a healthy rig + renders "—" in every free-text cell and hides the unbounded-column defect entirely); sections + read as separate cards; no horizontal scroll; any detail dialog opens with live data and closes + cleanly. Record PASS/FAIL per check in the plan or a recipe doc; CSS-only fixes may land in-session, anything structural gets recorded instead.