Files
ScadaBridge/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Audit/ExecutionTreePage.razor.cs
T
Joseph Doherty 7b0b9c7365 refactor: rename ScadaLink → ZB.MOM.WW.ScadaBridge (code + projects + namespaces)
Solution + 23 src projects + 26 test projects renamed; folders, csproj,
namespaces, and ScadaLinkDbContext/ScadaBridgeDbContext class updated.
ActorSystem "scadalink" → "scadabridge", Akka seed-node URLs migrated.
SQL roles/logins, LDAP domains, CLI command name, and CLI config dir
(~/.scadalink → ~/.scadabridge) also renamed.

Build green; 5 Host.Tests fail awaiting SQL login rename in next commit.
Pre-existing StaleTagMonitor timing flakes unchanged.

Rename script committed at tools/rename-to-scadabridge.sh.
2026-05-28 09:37:45 -04:00

122 lines
4.5 KiB
C#

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.WebUtilities;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Audit;
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.Audit;
/// <summary>
/// Code-behind for the execution-chain tree page (Audit Log ParentExecutionId
/// feature, Task 10). Route <c>/audit/execution-tree</c>, reached via the Audit
/// Log drilldown drawer's "View execution chain" action with
/// <c>?executionId={guid}</c>.
///
/// <para>
/// On initialization the page parses <c>?executionId=</c> (lax-parsed, matching
/// the Audit Log page's drill-in contract — an absent or unparseable value
/// leaves the page in a guidance state and issues NO service call), then asks
/// <see cref="ZB.MOM.WW.ScadaBridge.CentralUI.Services.IAuditLogQueryService.GetExecutionTreeAsync"/>
/// for the whole chain. The flat <see cref="ExecutionTreeNode"/> list is handed
/// to the recursive <c>ExecutionTree</c> component, which assembles + renders
/// the tree.
/// </para>
///
/// <para>
/// The data path mirrors the Audit Log results grid: the page talks ONLY to the
/// CentralUI <c>IAuditLogQueryService</c> facade, never <c>IAuditLogRepository</c>
/// directly, so the page can be unit-tested with a substituted service.
/// </para>
/// </summary>
public partial class ExecutionTreePage
{
[Inject] private NavigationManager Navigation { get; set; } = null!;
// The parsed ?executionId= value, or null when absent / unparseable.
private Guid? _executionId;
// The flat chain returned by the query service; null until the load
// completes (or when no id was supplied).
private IReadOnlyList<ExecutionTreeNode>? _nodes;
private bool _loading;
private string? _error;
// Execution-Tree Node Detail Modal feature (Task 4) — state backing the
// <ExecutionDetailModal>. A double-click on a tree node sets
// _modalExecutionId + flips _modalOpen true; the modal loads that
// execution's audit rows on the closed → open transition. _modalOpen is the
// visibility gate — _modalExecutionId is left intact across a close (it is
// harmless while the modal is hidden and avoids a flicker if reopened).
private Guid? _modalExecutionId;
private bool _modalOpen;
/// <inheritdoc />
protected override async Task OnInitializedAsync()
{
_executionId = ParseExecutionId();
if (_executionId is null)
{
// No id — render guidance, do not touch the service.
return;
}
await LoadChainAsync(_executionId.Value);
}
/// <summary>
/// Lax-parses <c>?executionId=</c>. Returns null when the param is absent or
/// is not a valid <see cref="Guid"/> — the page then shows guidance instead
/// of an error, consistent with the Audit Log page's drill-in handling.
/// </summary>
private Guid? ParseExecutionId()
{
var uri = Navigation.ToAbsoluteUri(Navigation.Uri);
var query = QueryHelpers.ParseQuery(uri.Query);
if (query.TryGetValue("executionId", out var values)
&& Guid.TryParse(values.ToString(), out var parsed))
{
return parsed;
}
return null;
}
private async Task LoadChainAsync(Guid executionId)
{
_loading = true;
_error = null;
try
{
_nodes = await AuditLogQueryService.GetExecutionTreeAsync(executionId);
}
catch (Exception ex)
{
// A transient DB outage degrades this page to an error banner
// rather than killing the circuit — the same defensive posture the
// Audit Log grid takes around its query.
_error = $"Could not load the execution chain: {ex.Message}";
_nodes = null;
}
finally
{
_loading = false;
}
}
/// <summary>
/// Raised by <c>ExecutionTree</c> (bubbled up from a node double-click) with
/// the activated node's <c>ExecutionId</c>. Opens the
/// <c>ExecutionDetailModal</c> for that execution — the modal loads its
/// audit rows on the closed → open transition.
/// </summary>
private void HandleNodeActivated(Guid executionId)
{
_modalExecutionId = executionId;
_modalOpen = true;
}
/// <summary>
/// Raised by <c>ExecutionDetailModal</c> when the user dismisses it. Flips
/// the visibility gate closed; <see cref="_modalExecutionId"/> is left as-is.
/// </summary>
private void HandleModalClose() => _modalOpen = false;
}