192 lines
9.0 KiB
Plaintext
192 lines
9.0 KiB
Plaintext
@using ScadaLink.Commons.Entities.Audit
|
|
@using ScadaLink.Commons.Types.Enums
|
|
|
|
@* 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. *@
|
|
|
|
@if (IsOpen && Event is not null)
|
|
{
|
|
<div class="offcanvas-backdrop fade show" data-test="drawer-backdrop"
|
|
@onclick="HandleClose"></div>
|
|
<div class="offcanvas offcanvas-end show audit-drilldown-drawer"
|
|
tabindex="-1"
|
|
style="visibility: visible;"
|
|
data-test="audit-drilldown-drawer">
|
|
<div class="offcanvas-header border-bottom">
|
|
<div>
|
|
<div class="text-muted small text-uppercase">Audit event</div>
|
|
<h5 class="offcanvas-title mb-0">Audit Event @ShortEventId(Event.EventId)</h5>
|
|
</div>
|
|
<button type="button" class="btn-close" aria-label="Close"
|
|
data-test="drawer-close"
|
|
@onclick="HandleClose"></button>
|
|
</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>
|
|
}
|
|
</div>
|
|
|
|
@* Action buttons 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">
|
|
Close
|
|
</button>
|
|
</div>
|
|
</div>
|
|
}
|