# 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 ``. 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`; 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`.
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 `` 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
- **bUnit** — `ExecutionTree` 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.