@page "/visualise" @page "/visualise/{View}" @using RackPeek.Domain.Graph.Serialisers @using RackPeek.Domain.Graph.UseCases @using Shared.Rcl.Components.Graphs @inject BuildPhysicalTopologyUseCase TopologyUseCase @inject BuildLogicalGraphUseCase LogicalUseCase @inject NavigationManager Nav @inject IJSRuntime JS Visualise
Knowledge Base

Visualise : @ActiveTitle

@if (_loading) {
loading inventory…
} else if (_error is not null) {
@_error
} else {
}
@code { [Parameter] public string? View { get; set; } private string ActiveView => string.Equals(View, "logical", StringComparison.OrdinalIgnoreCase) ? "logical" : "topology"; private string ActiveTitle => ActiveView switch { "logical" => "Logical — services & systems", _ => "Physical — hardware topology" }; private string? _source; private bool _loading = true; private string? _error; private string? _lastLoadedView; private readonly MermaidSerialiser _serialiser = new(); protected override async Task OnParametersSetAsync() { if (_lastLoadedView == ActiveView) return; _lastLoadedView = ActiveView; await LoadAsync(); } private async Task LoadAsync() { _loading = true; _error = null; StateHasChanged(); try { RackPeek.Domain.Graph.Graph graph = ActiveView switch { "logical" => await LogicalUseCase.ExecuteAsync(), _ => await TopologyUseCase.ExecuteAsync() }; _source = _serialiser.Serialise(graph); } catch (Exception ex) { _error = $"Failed to build graph: {ex.Message}"; _source = null; } finally { _loading = false; StateHasChanged(); } } private void SelectView(string view) { Nav.NavigateTo($"visualise/{view}"); } private const string HostId = "visualise-graph-host"; private bool CannotExport => _loading || _source is null; private string ExportButtonClass => "px-3 py-1 rounded border border-zinc-800 text-zinc-400 " + "hover:text-emerald-400 hover:border-zinc-700 disabled:opacity-40 disabled:hover:text-zinc-400"; private string ExportFilenameBase => ActiveView == "logical" ? "rackpeek-logical" : "rackpeek-topology"; private async Task ExportPngAsync() { if (CannotExport) return; await JS.InvokeVoidAsync( "rackpeekGraph.downloadPng", HostId, $"{ExportFilenameBase}.png", ExportBackground, 2); } private const string ExportBackground = "#09090b"; private async Task ExportSourceAsync() { if (CannotExport || _source is null) return; await JS.InvokeVoidAsync( "rackpeekGraph.downloadText", _source, $"{ExportFilenameBase}.mmd", "text/plain"); } private string TabClass(string view) => ActiveView == view ? "px-3 py-1 rounded border border-emerald-500/40 bg-emerald-500/10 text-emerald-400" : "px-3 py-1 rounded border border-zinc-800 text-zinc-400 hover:text-emerald-400 hover:border-zinc-700"; }