| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- @using RackPeek.Domain.Persistence
- @using RackPeek.Domain.Resources
- @inject IResourceCollection Repo
- <div class="text-sm text-zinc-300 flex items-stretch gap-3">
- @for (var i = 0; i < Levels.Count; i++)
- {
- if (i > 0)
- {
- <div class="flex items-center text-zinc-500">/</div>
- }
- <div class="flex">
- <CrumbLevel Items="Levels[i]" />
- </div>
- }
- </div>
- @code {
- [Parameter, EditorRequired]
- public ResourceType ResourceType { get; set; }
-
- public string Kind { get; set; }
- [Parameter, EditorRequired]
- public string ResourceName { get; set; } = default!;
- private List<List<Breadcrumb>> 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<string>();
- var byDistance = await BuildAncestorGraph(parents);
- RenderLevels(byDistance);
- AddLevel(new Breadcrumb(
- name,
- kind,
- Resource.GetResourceUrl(kind, name)));
- }
- private void RenderLevels(Dictionary<int, List<(string Name, string Kind)>> 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<Dictionary<int, List<(string Name, string Kind)>>>
- BuildAncestorGraph(IEnumerable<string> startingNodes)
- {
- var byDistance = new Dictionary<int, List<(string, string)>>();
- var visited = new HashSet<string>(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<string>())
- .Where(x => !string.IsNullOrWhiteSpace(x))
- .Distinct(StringComparer.OrdinalIgnoreCase))
- {
- queue.Enqueue((parent, dist + 1));
- }
- }
- }
- return byDistance;
- }
- private void AddLevel(params Breadcrumb[] items)
- => AddLevel((IEnumerable<Breadcrumb>)items);
- private void AddLevel(IEnumerable<Breadcrumb> items)
- {
- var list = items.ToList();
- if (list.Count > 0)
- Levels.Add(list);
- }
- public record Breadcrumb(string Label, string Kind, string Href);
- }
|