refactor(centralui): extract AuditEventDetail from AuditDrilldownDrawer
This commit is contained in:
165
src/ScadaLink.CentralUI/Components/Audit/AuditEventDetail.razor
Normal file
165
src/ScadaLink.CentralUI/Components/Audit/AuditEventDetail.razor
Normal file
@@ -0,0 +1,165 @@
|
||||
@using ScadaLink.Commons.Entities.Audit
|
||||
@using ScadaLink.Commons.Types.Enums
|
||||
|
||||
@* Reusable single-AuditEvent detail body (#23 M7 Bundle C / M7-T4..T8).
|
||||
Extracted from AuditDrilldownDrawer so the drawer and the execution-tree
|
||||
node-detail modal share one rendering of a row's detail.
|
||||
All form/field rendering follows the form-layout memory:
|
||||
read-only fields first (definition list), then subsections stacked,
|
||||
action buttons at the bottom. *@
|
||||
|
||||
@* 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>
|
||||
}
|
||||
|
||||
@* Action buttons at the bottom per form-layout memory. *@
|
||||
<div class="d-flex gap-2 flex-wrap" data-test="audit-event-detail-actions">
|
||||
@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>
|
||||
}
|
||||
</div>
|
||||
Reference in New Issue
Block a user