ResourceBreadCrumbComponent.razor 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. @using RackPeek.Domain.Persistence
  2. @using RackPeek.Domain.Resources
  3. @inject IResourceCollection Repo
  4. <div class="text-sm text-zinc-300 flex items-stretch gap-3">
  5. @for (var i = 0; i < Levels.Count; i++)
  6. {
  7. if (i > 0)
  8. {
  9. <div class="flex items-center text-zinc-500">/</div>
  10. }
  11. <div class="flex">
  12. <CrumbLevel Items="Levels[i]"/>
  13. </div>
  14. }
  15. </div>
  16. @code {
  17. [Parameter] [EditorRequired] public ResourceType ResourceType { get; set; }
  18. public string Kind { get; set; } = string.Empty;
  19. [Parameter] [EditorRequired] public string ResourceName { get; set; } = default!;
  20. private List<List<Breadcrumb>> Levels { get; } = new();
  21. protected override async Task OnParametersSetAsync()
  22. {
  23. Kind = await Repo.GetKind(ResourceName) ?? "";
  24. Levels.Clear();
  25. switch (ResourceType)
  26. {
  27. case ResourceType.Hardware:
  28. AddLevel(new Breadcrumb(
  29. ResourceName,
  30. Kind,
  31. Resource.GetResourceUrl("hardware", ResourceName)));
  32. break;
  33. case ResourceType.System:
  34. await BuildFromNode(ResourceName, "system");
  35. break;
  36. case ResourceType.Service:
  37. await BuildFromNode(ResourceName, "service");
  38. break;
  39. }
  40. }
  41. private async Task BuildFromNode(string name, string kind)
  42. {
  43. var resource = await Repo.GetByNameAsync(name);
  44. var parents = resource?.RunsOn ?? Enumerable.Empty<string>();
  45. var byDistance = await BuildAncestorGraph(parents);
  46. RenderLevels(byDistance);
  47. AddLevel(new Breadcrumb(
  48. name,
  49. kind,
  50. Resource.GetResourceUrl(kind, name)));
  51. }
  52. private void RenderLevels(Dictionary<int, List<(string Name, string Kind)>> byDistance)
  53. {
  54. foreach (var dist in byDistance.Keys.OrderByDescending(x => x))
  55. {
  56. var items = byDistance[dist];
  57. var hardware = items
  58. .Where(x => Resource.IsHardware(x.Kind))
  59. .OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase)
  60. .Select(x => new Breadcrumb(
  61. x.Name,
  62. x.Kind,
  63. Resource.GetResourceUrl(x.Kind, x.Name)));
  64. var systems = items
  65. .Where(x => x.Kind == "system")
  66. .OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase)
  67. .Select(x => new Breadcrumb(
  68. x.Name,
  69. x.Kind,
  70. Resource.GetResourceUrl(x.Kind, x.Name)));
  71. AddLevel(hardware);
  72. AddLevel(systems);
  73. }
  74. }
  75. private async Task<Dictionary<int, List<(string Name, string Kind)>>>
  76. BuildAncestorGraph(IEnumerable<string> startingNodes)
  77. {
  78. var byDistance = new Dictionary<int, List<(string, string)>>();
  79. var visited = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
  80. var queue = new Queue<(string Name, int Dist)>();
  81. foreach (var node in startingNodes
  82. .Where(x => !string.IsNullOrWhiteSpace(x))
  83. .Distinct(StringComparer.OrdinalIgnoreCase))
  84. {
  85. queue.Enqueue((node, 1));
  86. }
  87. while (queue.Count > 0)
  88. {
  89. var (name, dist) = queue.Dequeue();
  90. if (!visited.Add(name))
  91. continue;
  92. var kind = await Repo.GetKind(name);
  93. if (kind is null)
  94. continue;
  95. kind = kind.Trim().ToLowerInvariant();
  96. if (!byDistance.TryGetValue(dist, out var list))
  97. {
  98. list = new List<(string, string)>();
  99. byDistance[dist] = list;
  100. }
  101. list.Add((name, kind));
  102. if (kind == "system")
  103. {
  104. var res = await Repo.GetByNameAsync(name);
  105. foreach (var parent in (res?.RunsOn ?? Enumerable.Empty<string>())
  106. .Where(x => !string.IsNullOrWhiteSpace(x))
  107. .Distinct(StringComparer.OrdinalIgnoreCase))
  108. {
  109. queue.Enqueue((parent, dist + 1));
  110. }
  111. }
  112. }
  113. return byDistance;
  114. }
  115. private void AddLevel(params Breadcrumb[] items)
  116. {
  117. AddLevel((IEnumerable<Breadcrumb>)items);
  118. }
  119. private void AddLevel(IEnumerable<Breadcrumb> items)
  120. {
  121. var list = items.ToList();
  122. if (list.Count > 0)
  123. Levels.Add(list);
  124. }
  125. public record Breadcrumb(string Label, string Kind, string Href);
  126. }