refactor(centralui): extract AuditEventDetail from AuditDrilldownDrawer
This commit is contained in:
@@ -3,9 +3,9 @@
|
||||
|
||||
@* Audit Log drilldown drawer (#23 M7 Bundle C / M7-T4..T8).
|
||||
Right-side Bootstrap offcanvas-style drawer hosted by the Audit Log page.
|
||||
All form/field rendering follows the form-layout memory:
|
||||
read-only fields first (definition list), then subsections stacked,
|
||||
action buttons at the bottom of the drawer. *@
|
||||
The drawer owns only the offcanvas chrome (backdrop, header, Close buttons);
|
||||
the single-AuditEvent detail body is delegated to <AuditEventDetail>, which
|
||||
is shared with the execution-tree node-detail modal. *@
|
||||
|
||||
@if (IsOpen && Event is not null)
|
||||
{
|
||||
@@ -26,161 +26,12 @@
|
||||
</div>
|
||||
|
||||
<div class="offcanvas-body small">
|
||||
@* Read-only field list — primary identification + provenance. *@
|
||||
<dl class="row mb-3" data-test="drawer-fields">
|
||||
<dt class="col-4 text-muted fw-normal">Channel / Kind</dt>
|
||||
<dd class="col-8" data-test="field-Channel">@Event.Channel / @Event.Kind</dd>
|
||||
|
||||
<dt class="col-4 text-muted fw-normal">Status</dt>
|
||||
<dd class="col-8" data-test="field-Status">@Event.Status</dd>
|
||||
|
||||
<dt class="col-4 text-muted fw-normal">HttpStatus</dt>
|
||||
<dd class="col-8 font-monospace" data-test="field-HttpStatus">@(Event.HttpStatus?.ToString() ?? "—")</dd>
|
||||
|
||||
<dt class="col-4 text-muted fw-normal">Target</dt>
|
||||
<dd class="col-8" data-test="field-Target">@(Event.Target ?? "—")</dd>
|
||||
|
||||
<dt class="col-4 text-muted fw-normal">Actor</dt>
|
||||
<dd class="col-8" data-test="field-Actor">@(Event.Actor ?? "—")</dd>
|
||||
|
||||
<dt class="col-4 text-muted fw-normal">SourceSiteId</dt>
|
||||
<dd class="col-8" data-test="field-SourceSiteId">@(Event.SourceSiteId ?? "—")</dd>
|
||||
|
||||
<dt class="col-4 text-muted fw-normal">SourceInstanceId</dt>
|
||||
<dd class="col-8" data-test="field-SourceInstanceId">@(Event.SourceInstanceId ?? "—")</dd>
|
||||
|
||||
<dt class="col-4 text-muted fw-normal">SourceScript</dt>
|
||||
<dd class="col-8" data-test="field-SourceScript">@(Event.SourceScript ?? "—")</dd>
|
||||
|
||||
<dt class="col-4 text-muted fw-normal">CorrelationId</dt>
|
||||
<dd class="col-8 font-monospace" data-test="field-CorrelationId">@(Event.CorrelationId?.ToString() ?? "—")</dd>
|
||||
|
||||
<dt class="col-4 text-muted fw-normal">ExecutionId</dt>
|
||||
<dd class="col-8 font-monospace" data-test="field-ExecutionId">@(Event.ExecutionId?.ToString() ?? "—")</dd>
|
||||
|
||||
<dt class="col-4 text-muted fw-normal">ParentExecutionId</dt>
|
||||
<dd class="col-8 font-monospace" data-test="field-ParentExecutionId">@(Event.ParentExecutionId?.ToString() ?? "—")</dd>
|
||||
|
||||
<dt class="col-4 text-muted fw-normal">OccurredAtUtc</dt>
|
||||
<dd class="col-8 font-monospace" data-test="field-OccurredAtUtc">@FormatTimestamp(Event.OccurredAtUtc)</dd>
|
||||
|
||||
<dt class="col-4 text-muted fw-normal">IngestedAtUtc</dt>
|
||||
<dd class="col-8 font-monospace" data-test="field-IngestedAtUtc">@(Event.IngestedAtUtc.HasValue ? FormatTimestamp(Event.IngestedAtUtc.Value) : "—")</dd>
|
||||
|
||||
<dt class="col-4 text-muted fw-normal">DurationMs</dt>
|
||||
<dd class="col-8 font-monospace" data-test="field-DurationMs">@(Event.DurationMs?.ToString() ?? "—")</dd>
|
||||
</dl>
|
||||
|
||||
@* Error subsection — only shown when there is something to report. *@
|
||||
@if (!string.IsNullOrEmpty(Event.ErrorMessage) || !string.IsNullOrEmpty(Event.ErrorDetail))
|
||||
{
|
||||
<section class="mb-3" data-test="section-error">
|
||||
<h6 class="text-uppercase text-muted small fw-semibold mb-1">Error</h6>
|
||||
@if (!string.IsNullOrEmpty(Event.ErrorMessage))
|
||||
{
|
||||
<p class="text-danger mb-1">@Event.ErrorMessage</p>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Event.ErrorDetail))
|
||||
{
|
||||
<pre class="bg-light border rounded p-2 mb-0 drawer-pre">@Event.ErrorDetail</pre>
|
||||
}
|
||||
</section>
|
||||
}
|
||||
|
||||
@* Request body (channel-aware renderer). *@
|
||||
@if (!string.IsNullOrEmpty(Event.RequestSummary))
|
||||
{
|
||||
<section class="mb-3" data-test="section-request">
|
||||
<h6 class="text-uppercase text-muted small fw-semibold mb-1 d-flex align-items-center gap-2">
|
||||
<span>Request</span>
|
||||
@if (IsRedacted(Event.RequestSummary))
|
||||
{
|
||||
<span data-test="redaction-badge-request"
|
||||
class="badge bg-warning text-dark"
|
||||
title="Sensitive values redacted by audit pipeline">
|
||||
Redacted
|
||||
</span>
|
||||
}
|
||||
</h6>
|
||||
<div data-test="request-body">
|
||||
@RenderBody(Event.RequestSummary!, Event.Channel)
|
||||
</div>
|
||||
</section>
|
||||
}
|
||||
|
||||
@* Response body (channel-aware renderer). *@
|
||||
@if (!string.IsNullOrEmpty(Event.ResponseSummary))
|
||||
{
|
||||
<section class="mb-3" data-test="section-response">
|
||||
<h6 class="text-uppercase text-muted small fw-semibold mb-1 d-flex align-items-center gap-2">
|
||||
<span>Response</span>
|
||||
@if (IsRedacted(Event.ResponseSummary))
|
||||
{
|
||||
<span data-test="redaction-badge-response"
|
||||
class="badge bg-warning text-dark"
|
||||
title="Sensitive values redacted by audit pipeline">
|
||||
Redacted
|
||||
</span>
|
||||
}
|
||||
</h6>
|
||||
<div data-test="response-body">
|
||||
@RenderBody(Event.ResponseSummary!, Event.Channel)
|
||||
</div>
|
||||
</section>
|
||||
}
|
||||
|
||||
@* Extra is always JSON when present. *@
|
||||
@if (!string.IsNullOrEmpty(Event.Extra))
|
||||
{
|
||||
<section class="mb-3" data-test="section-extra">
|
||||
<h6 class="text-uppercase text-muted small fw-semibold mb-1">Extra</h6>
|
||||
<pre class="bg-light border rounded p-2 mb-0 drawer-pre json">@PrettyPrintJson(Event.Extra!)</pre>
|
||||
</section>
|
||||
}
|
||||
@* Single-row detail body + action buttons — shared component. *@
|
||||
<AuditEventDetail Event="Event" />
|
||||
</div>
|
||||
|
||||
@* Action buttons at the bottom per form-layout memory. *@
|
||||
@* Close button kept at the bottom per form-layout memory. *@
|
||||
<div class="border-top p-3 d-flex gap-2 flex-wrap drawer-footer">
|
||||
@if (IsApiChannel(Event.Channel))
|
||||
{
|
||||
<button class="btn btn-outline-secondary btn-sm"
|
||||
data-test="copy-as-curl"
|
||||
@onclick="CopyCurl">
|
||||
Copy as cURL
|
||||
</button>
|
||||
}
|
||||
@if (Event.CorrelationId is not null)
|
||||
{
|
||||
<button class="btn btn-outline-secondary btn-sm"
|
||||
data-test="show-all-events"
|
||||
@onclick="ShowAllForOperation">
|
||||
Show all events for this operation
|
||||
</button>
|
||||
}
|
||||
@if (Event.ExecutionId is not null)
|
||||
{
|
||||
<button class="btn btn-outline-secondary btn-sm"
|
||||
data-test="view-this-execution"
|
||||
@onclick="ViewThisExecution">
|
||||
View this execution
|
||||
</button>
|
||||
}
|
||||
@if (Event.ParentExecutionId is not null)
|
||||
{
|
||||
<button class="btn btn-outline-secondary btn-sm"
|
||||
data-test="view-parent-execution"
|
||||
@onclick="ViewParentExecution">
|
||||
View parent execution
|
||||
</button>
|
||||
}
|
||||
@if (Event.ExecutionId is not null)
|
||||
{
|
||||
<button class="btn btn-outline-secondary btn-sm"
|
||||
data-test="view-execution-chain"
|
||||
@onclick="ViewExecutionChain">
|
||||
View execution chain
|
||||
</button>
|
||||
}
|
||||
<button class="btn btn-primary btn-sm ms-auto"
|
||||
data-test="drawer-close-footer"
|
||||
@onclick="HandleClose">
|
||||
|
||||
Reference in New Issue
Block a user