7b0b9c7365
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.
122 lines
4.5 KiB
C#
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;
|
|
}
|