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;
///
/// Code-behind for the execution-chain tree page (Audit Log ParentExecutionId
/// feature, Task 10). Route /audit/execution-tree, reached via the Audit
/// Log drilldown drawer's "View execution chain" action with
/// ?executionId={guid}.
///
///
/// On initialization the page parses ?executionId= (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
///
/// for the whole chain. The flat list is handed
/// to the recursive ExecutionTree component, which assembles + renders
/// the tree.
///
///
///
/// The data path mirrors the Audit Log results grid: the page talks ONLY to the
/// CentralUI IAuditLogQueryService facade, never IAuditLogRepository
/// directly, so the page can be unit-tested with a substituted service.
///
///
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? _nodes;
private bool _loading;
private string? _error;
// Execution-Tree Node Detail Modal feature (Task 4) — state backing the
// . 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;
///
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);
}
///
/// Lax-parses ?executionId=. Returns null when the param is absent or
/// is not a valid — the page then shows guidance instead
/// of an error, consistent with the Audit Log page's drill-in handling.
///
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;
}
}
///
/// Raised by ExecutionTree (bubbled up from a node double-click) with
/// the activated node's ExecutionId. Opens the
/// ExecutionDetailModal for that execution — the modal loads its
/// audit rows on the closed → open transition.
///
private void HandleNodeActivated(Guid executionId)
{
_modalExecutionId = executionId;
_modalOpen = true;
}
///
/// Raised by ExecutionDetailModal when the user dismisses it. Flips
/// the visibility gate closed; is left as-is.
///
private void HandleModalClose() => _modalOpen = false;
}