From 9e337efa0ad36ebfbb6db018af51089e4ae8785e Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 10 Aug 2026 17:56:27 -0400 Subject: [PATCH] =?UTF-8?q?docs(plan):=20AdminUI=20density=20sweep=20?= =?UTF-8?q?=E2=80=94=20=C2=A71d=20scan=20findings=20+=203-task=20execution?= =?UTF-8?q?=20plan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plans/2026-08-10-adminui-density-sweep.md | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 docs/plans/2026-08-10-adminui-density-sweep.md diff --git a/docs/plans/2026-08-10-adminui-density-sweep.md b/docs/plans/2026-08-10-adminui-density-sweep.md new file mode 100644 index 00000000..f2784d39 --- /dev/null +++ b/docs/plans/2026-08-10-adminui-density-sweep.md @@ -0,0 +1,65 @@ +# AdminUI density/layout sweep (§1d follow-through) — 2026-08-10 + +Follow-through of the `/hosts` density rework: the §1d scan (four parallel read-only agents, +62 files) applied to the rest of the AdminUI. Playbook: `../scadaproj/admin_ui_cleanup.md` +§1d + §2 "Density fixes". Prior context: `2026-08-10-adminui-ui-sweeps.md` (structure sweep) +and the `/hosts` rework commit `34d60c41`. + +## Scan verdict + +- **Zero unguarded `[..N]` slices** anywhere (all existing slices are length-guarded or Guid-fixed). +- Cluster pages: no A/B/D defects at all; 3 C items. +- The dominant class is **C — unbounded backend/free text** (~17 sites), concentrated on the + fault-triage surfaces (`/alerts`, `/fleet`, `/script-log`, alarms-historian, DriverStatusPanel). +- Two structural defects: `Scripts.razor` (inline `
` source dump + identity slam — the + /hosts shape) and `EquipmentPage.razor` (only authoring page with no panel idiom + bare RawPath + column). +- Deliberate designs kept: `RawManualTagEntryModal` colspan config sub-row (documented, inside + `modal-xl` — a fix means stacked modals), `VirtualTagModal` inline Monaco card (collapsed by + default, own card boundary). +- Style-only residual (not §1d, not in this sweep): `Deployments.razor` is off the panel idiom + (bare `

` + Bootstrap table). + +## Task 1 — mechanical truncation sweep (Abbreviate + title) + +All `DisplayText.Abbreviate(value, N)` + full text on `title`; behavior byte-for-byte. + +| File:line | Value | N | +|---|---|---| +| `Pages/Alerts.razor:84` | alarm `Message` | 80 | +| `Pages/Fleet.razor:92` | `FailureReason` | 80 | +| `Pages/ScriptLog.razor:71` | log `Message` (mono) | 120 | +| `Pages/AlarmsHistorian.razor:41` | drain `LastError` | 80 | +| `Shared/Drivers/DriverStatusPanel.razor` | op-result chip — truncate in `ShowOpResult` choke point, `title` on chip; also `:41` SignalR `_error` | 60 | +| `Pages/Certificates.razor:77-78` | Subject / Issuer DNs | 48 | +| `Pages/RoleGrants.razor:65,96,24` | LDAP group DNs, SearchBase | 48 | +| `Pages/Clusters/ClusterAcls.razor:88` | ACL `Notes` | 60 | +| `Pages/Clusters/ClusterOverview.razor:49,100,102` | cluster `Notes` (120), `Host` (30), `ApplicationUri` (40) | — | +| `Pages/Clusters/ClusterRedundancy.razor:128` | `ApplicationUri` | 40 | +| `Pages/Account.razor:63` | group-DN chips | 40 | +| `Pages/Clusters/ClusterAudit.razor:55-57` | add `title` to the already-ellipsized DetailsJson cell | — | +| `Shared/Drivers/Pickers/SqlAddressPickerBody.razor:208-211` | composed TagConfig JSON `` | 64 | +| `Shared/Drivers/Pickers/SqlAddressPickerBody.razor:159-162` | `_openError` → adopt the sibling truncated-chip + `title` pattern | 60 | +| `Shared/Drivers/DriverTagPicker.razor:19-22` | footer `CurrentAddress` | 60 | +| Picker Result `` lines (AbCip:75, TwinCAT:64, OpcUaClient:57, Galaxy:95, FOCAS:109) | browsed address | 64 | + +## Task 2 — Scripts page: `
` expander → source dialog + two-line identity + +`Pages/Scripts.razor`: replace the per-row inline `
`/`
` source dump with the
+Bootstrap modal pattern (`modal-backdrop fade show` + `modal fade show d-block`, `modal-lg`,
+one at a time); list rows get the stacked identity (name first line, mono id + hash beneath,
+chips with the name). Hold only the open row's `ScriptId`; re-resolve from current data every
+render (self-closes if the script vanishes).
+
+## Task 3 — EquipmentPage: adopt the panel idiom + bound the RawPath column
+
+`Pages/Uns/EquipmentPage.razor`: wrap each tab body in `
` + +`panel-head` (Details tab: Identity and OPC 40010 as two panels, dropping the `
`), +matching ScriptEdit's shells; `Abbreviate(r.RawPath, 48)` + `title` at :166. Behavior +byte-for-byte — tab switching, all bindings, buttons, modals unchanged. + +## Verification + +Merged-tree full build + AdminUI.Tests; live gate on docker-dev (both centrals rebuilt) with +at least one faulted/erroring row on screen (§1d realistic-data rule): /alerts, /fleet, +/scripts dialog open/close, equipment page tabs, Sql picker footer.