@using RackPeek.Domain.Persistence @using RackPeek.Domain.Resources @inject IResourceCollection Repo
@for (var i = 0; i < Levels.Count; i++) { if (i > 0) {
/
}
}
@code { [Parameter] [EditorRequired] public ResourceType ResourceType { get; set; } public string Kind { get; set; } = string.Empty; [Parameter] [EditorRequired] public string ResourceName { get; set; } = default!; private List> Levels { get; } = new(); protected override async Task OnParametersSetAsync() { Kind = await Repo.GetKind(ResourceName) ?? ""; Levels.Clear(); switch (ResourceType) { case ResourceType.Hardware: AddLevel(new Breadcrumb( ResourceName, Kind, Resource.GetResourceUrl("hardware", ResourceName))); break; case ResourceType.System: await BuildFromNode(ResourceName, "system"); break; case ResourceType.Service: await BuildFromNode(ResourceName, "service"); break; } } private async Task BuildFromNode(string name, string kind) { var resource = await Repo.GetByNameAsync(name); var parents = resource?.RunsOn ?? Enumerable.Empty(); var byDistance = await BuildAncestorGraph(parents); RenderLevels(byDistance); AddLevel(new Breadcrumb( name, kind, Resource.GetResourceUrl(kind, name))); } private void RenderLevels(Dictionary> byDistance) { foreach (var dist in byDistance.Keys.OrderByDescending(x => x)) { var items = byDistance[dist]; var hardware = items .Where(x => Resource.IsHardware(x.Kind)) .OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase) .Select(x => new Breadcrumb( x.Name, x.Kind, Resource.GetResourceUrl(x.Kind, x.Name))); var systems = items .Where(x => x.Kind == "system") .OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase) .Select(x => new Breadcrumb( x.Name, x.Kind, Resource.GetResourceUrl(x.Kind, x.Name))); AddLevel(hardware); AddLevel(systems); } } private async Task>> BuildAncestorGraph(IEnumerable startingNodes) { var byDistance = new Dictionary>(); var visited = new HashSet(StringComparer.OrdinalIgnoreCase); var queue = new Queue<(string Name, int Dist)>(); foreach (var node in startingNodes .Where(x => !string.IsNullOrWhiteSpace(x)) .Distinct(StringComparer.OrdinalIgnoreCase)) { queue.Enqueue((node, 1)); } while (queue.Count > 0) { var (name, dist) = queue.Dequeue(); if (!visited.Add(name)) continue; var kind = await Repo.GetKind(name); if (kind is null) continue; kind = kind.Trim().ToLowerInvariant(); if (!byDistance.TryGetValue(dist, out var list)) { list = new List<(string, string)>(); byDistance[dist] = list; } list.Add((name, kind)); if (kind == "system") { var res = await Repo.GetByNameAsync(name); foreach (var parent in (res?.RunsOn ?? Enumerable.Empty()) .Where(x => !string.IsNullOrWhiteSpace(x)) .Distinct(StringComparer.OrdinalIgnoreCase)) { queue.Enqueue((parent, dist + 1)); } } } return byDistance; } private void AddLevel(params Breadcrumb[] items) { AddLevel((IEnumerable)items); } private void AddLevel(IEnumerable items) { var list = items.ToList(); if (list.Count > 0) Levels.Add(list); } public record Breadcrumb(string Label, string Kind, string Href); }