Files
scadalink-design/docs/plans/2026-05-22-execution-tree-node-modal-design.md
2026-05-22 01:13:11 -04:00

4.9 KiB

Execution-Tree Node Detail Modal (Design)

Date: 2026-05-22 Status: Validated — ready for implementation planning.

Problem

On the Central UI execution-chain tree page (/audit/execution-tree, the ParentExecutionId feature's Task 10), each node represents one execution and shows a small inline summary. The only interaction is the short-id link, which navigates away to /audit/log?executionId=…. There is no way to inspect an execution's actual audit rows without leaving the tree.

Decision

Double-clicking a tree node opens a modal showing that execution's audit rows. The modal mirrors the /audit/log detail experience: a list of the execution's rows, and clicking a row reveals that row's full field/payload detail — the exact content the Audit Log drilldown drawer shows.

Resolved during brainstorming:

  • Modal content — the execution's audit rows, with per-row full detail.
  • Multi-row executions — list the rows; clicking one shows its detail. A single-row execution opens straight to the detail view.
  • Trigger — double-click anywhere on the node. The short-id link keeps its single-click navigation to the Audit Log grid (unchanged).

Considered and rejected

  • Reuse AuditDrilldownDrawer directly. The drawer renders one AuditEvent by design; bending it into a list-or-detail hybrid is more invasive to a well-tested component than a purpose-built modal.
  • Inline expansion under the node. The user asked for a modal, and an inline panel inside the recursive tree fights the existing expand/collapse toggle and is visually messy.

Components

Component Change
AuditEventDetail.razor New. The single-AuditEvent field/payload/drill-in-button block, extracted verbatim from AuditDrilldownDrawer's body.
AuditDrilldownDrawer.razor Modified. Keeps its offcanvas chrome + close button; its body becomes <AuditEventDetail Event="Event" />. The one refactor with regression risk — existing drawer bUnit + Playwright tests guard it.
ExecutionDetailModal.razor (+ .razor.cs + .razor.css) New. A custom Bootstrap modal — hand-rolled modal / modal-backdrop markup, Blazor-toggled, no component framework (the same way AuditDrilldownDrawer hand-rolls offcanvas).
ExecutionTree.razor / .razor.cs Modified. @ondblclick on the node body invokes a new OnNodeActivated EventCallback<Guid>; recursive child instances re-raise it upward so the event bubbles to the root.
ExecutionTreePage.razor / .razor.cs Modified. Hosts one ExecutionDetailModal; wires the tree's OnNodeActivated to open it.

No database, repository, or service changes — purely Central UI. The IAuditLogQueryService.QueryAsync method already filters by ExecutionId; the modal reuses it (no new service method).

Data flow

  1. Double-click a node → ExecutionTree invokes OnNodeActivated(node.ExecutionId).
  2. The event bubbles up the recursive ExecutionTree instances to ExecutionTreePage.
  3. The page opens ExecutionDetailModal with the ExecutionId.
  4. The modal calls IAuditLogQueryService.QueryAsync(new AuditLogQueryFilter(ExecutionId: id), new AuditLogPaging(PageSize: 100))IReadOnlyList<AuditEvent>.
  5. Render by row count:
    • ≥ 2 rows — a compact row list (kind / status / target / time, each row a button); clicking a row swaps to its <AuditEventDetail> with a "← Back to rows" control.
    • 1 row — opens straight to the detail view.
    • 0 rows — a stub execution; a friendly empty state.
  6. Close via the X button, the backdrop, or Esc.

The list rows are full AuditEvent objects (that is what QueryAsync returns), so the list→detail transition needs no second fetch.

Error handling

  • A QueryAsync failure surfaces an inline error inside the modal ("Couldn't load this execution's rows") and never tears down the SignalR circuit — mirroring the tree page's existing try/catch degrade-gracefully pattern.
  • An empty result renders the friendly empty state, not an error.

Testing

  • bUnitExecutionTree raises OnNodeActivated on @ondblclick and bubbles it through a nested instance; ExecutionDetailModal list renders from a fake query service, row click → detail, 1-row jump-straight, 0-row empty state, close; AuditEventDetail renders the field block; the existing AuditDrilldownDrawer tests stay green after the body extraction.
  • Playwright — on /audit/execution-tree, double-click a node → modal opens → (multi-row) row list → click a row → detail → close. Uses a seeded chain.
  • frontend-design skill for the modal markup/CSS — clean corporate aesthetic, custom Blazor + Bootstrap, no component frameworks.

Constraints

  • Central UI only — no DB / repository / service-contract changes.
  • Custom Blazor + Bootstrap; no component frameworks.
  • The short-id link's single-click navigation to /audit/log?executionId=… is unchanged.