Files
ScadaBridge/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Audit/AuditEventDetail.razor
T
Joseph Doherty 410349767d style(centralui): full-app bg-light/bg-white -> theme-aware utility sweep (M10 residual)
T34c only fixed the bounded modal-surface offenders. Bootstrap 5.3's bg-light
and bg-white are fixed light values that do NOT flip under [data-bs-theme=dark],
so every remaining use was a dark-mode contrast break. 35 swaps across 19 files:

  surface / <pre> / <code>  bg-light                -> bg-body-secondary
  panel                     bg-white                -> bg-body
  neutral badge             bg-light text-dark      -> bg-secondary-subtle text-secondary-emphasis
  muted badge / input group bg-light text-muted     -> bg-body-secondary text-body-secondary

DELIBERATELY LEFT (7 sites): the neutral member of a status-badge switch or
ternary whose siblings are all solid, non-theme-aware colours (bg-success,
bg-danger, bg-warning) — swapping only the neutral one to a subtle token breaks
the visual weight of the set, so these stay until the whole family is retoned:
  Topology.razor:513 (Current badge) and :588 (InstanceState.NotDeployed)
  InstanceConfigure.razor:1520 (same InstanceState switch)
  NotificationReport.razor StatusBadgeClass fallback
  TransportImport.razor ConflictKind badge fallback
  SecuredWrites.razor text-bg-light fallback (a text-bg-* family with no
    theme-aware member at all)
  Health.razor:377 depth ternary
Also untouched by design: SchemaBuilder.razor:88 (already bg-light-subtle,
theme-aware), bg-dark text-light console panels, and site.css / #reconnect-modal.

InstanceConfigure.razor is edited concurrently elsewhere; its change here is 5
pure class-string swaps on existing lines, no reflow. No test asserted any of
these classes. Also marks all four M10 residuals done in the plan doc.
2026-08-01 11:28:20 -04:00

169 lines
6.8 KiB
Plaintext

@using ZB.MOM.WW.ScadaBridge.CentralUI.Services
@using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums
@* Reusable single-AuditEvent detail body.
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">SourceNode</dt>
<dd class="col-8" data-test="field-SourceNode">@(Event.SourceNode ?? "—")</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-body-secondary 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-body-secondary 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>